/ * Prototipos JavaScript marco, versión 1.6.0.2
 * (C) 2005-2008 Sam Stephenson
 *
 * Prototipo es de libre distribución bajo los términos de una licencia tipo MIT.
 * Para más información, consulte el sitio web Prototipo: http://www.prototypejs.org/
 *
 * ------------------------------------------------- ------------------------- * /

Prototipo var = {
  Versión: '1 .6.0.2 ',

  Navegador: {
    IE:! (&& Window.attachEvent window.opera!)
    Opera:! Window.opera,
    WebKit: navigator.userAgent.indexOf ('AppleWebKit / ")> -1,
    Gecko: navigator.userAgent.indexOf ('Gecko')> -1 navigator.userAgent.indexOf && ('KHTML') == -1,
    MobileSafari:! Navigator.userAgent.match (.. / Apple Safari * Móvil * /)
  },

  BrowserFeatures: {
    XPath:! Document.evaluate,
    ElementExtensions:! Window.HTMLElement,
    SpecificElementExtensions:
      document.createElement ('div'). __proto__ &&
      document.createElement ('div'). __proto__! ==
        document.createElement ('form'). __proto__
  },

  ScriptFragment: '<script[^>] *> (? [\ \ S \ \ s] *) <\ / script>',
  JSONFilter: / ^ \ / \ *-secure-([\ s \ S] *) \ * \ / \ s * $ /,

  emptyFunction: function () {}
  K: function (x) {return x}
};

if (Prototype.Browser.MobileSafari)
  Prototype.BrowserFeatures.SpecificElementExtensions = false;


/ * Basado en la implementación herencia Alex Arnell. * /
var = La clase {
  crear: function () {
    var parent = null, properties = $ A (argumentos);
    if (Object.isFunction (propiedades [0]))
      parent = properties.shift ();

    klass función () {
      this.initialize.apply (esto, argumentos);
    }

    Object.extend (klass, Class.Methods);
    klass.superclass padre =;
    klass.subclasses = [];

    if (padre) {
      var subclase = function () {};
      subclass.prototype = parent.prototype;
      klass.prototype = nueva subclase;
      parent.subclasses.push (klass);
    }

    for (var i = 0; i <properties.length, i + +)
      klass.addMethods (propiedades [i]);

    if (! klass.prototype.initialize)
      klass.prototype.initialize Prototype.emptyFunction =;

    klass.prototype.constructor = klass;

    volver klass;
  }
};

Class.Methods = {
  addMethods: function (fuente) {
    var = antepasado this.superclass this.superclass.prototype &&;
    propiedades var = Object.keys (fuente);

    if (Object.keys ({toString: true}!). longitud)
      properties.push ("toString", "valueOf");

    for (var i = 0, longitud = properties.length; longitud i <; i + +) {
      var = propiedad propiedades [i], valor = fuente [de la propiedad];
      if (&& antepasado Object.isFunction (valor) &&
          value.argumentNames (). primero () == "$ super") {
        var método = valor, valor = Object.extend ((función de (m) {
          función de retorno () {return antepasado [m] se aplican (esto, argumentos).};
        }) (Propiedad). Wrap (método), {
          valueOf: function () {return} método,
          toString: function () {return method.toString ()}
        });
      }
      this.prototype [propiedad] = valor;
    }

    devolver este;
  }
};

Resumen var = {};

Object.extend = function (destino, fuente) {
  for (var propiedad en la fuente)
    destino [propiedad] = Fuente [propiedad];
  volver destino;
};

Object.extend (Object, {
  inspeccionar: function (objeto) {
    try {
      if (Object.isUndefined (objeto)) return "indefinido";
      if (objeto === null) return 'nulo';
      volver object.inspect? object.inspect (): String (objeto);
    Captura} (e) {
      if (e instanceof RangeError) return '...';
      tirar e;
    }
  },

  toJSON: function (objeto) {
    var type = typeof objeto;
    switch (tipo) {
      case 'no definido':
      case 'función':
      'desconocido' caso: el regreso;
      'boolean' caso: Object.toString return ();
    }

    if (objeto === null) return 'nulo';
    if (object.toJSON) return object.toJSON ();
    if (Object.isElement (objeto)) return;

    resultados var = [];
    para (propiedad en el objeto var) {
      var = valor Object.toJSON (objeto [de la propiedad]);
      if (! Object.isUndefined (valor))
        results.push (property.toJSON () + ":" + valor);
    }

    return '{' + results.join (',') + '}';
  },

  toQueryString: function (objeto) {
    return $ H (objeto) toQueryString ().;
  },

  ToHTML: function (objeto) {
    regresar objeto object.toHTML &&? object.toHTML (): String.interpret (objeto);
  },

  Teclas de función: (objeto) {
    Teclas var = [];
    para (propiedad var en objeto)
      keys.push (propiedad);
    devolver las llaves;
  },

  valores: function (objeto) {
    valores var = [];
    para (propiedad var en objeto)
      values.push (objeto [de la propiedad]);
    devolver valores;
  },

  clon: function (objeto) {
    volver Object.extend ({}, objeto);
  },

  isElement: function (objeto) {
    volver && objeto object.nodeType == 1;
  },

  isArray: function (objeto) {
    Objeto de Retorno! = && == typeof objeto nulo "objeto" &&
      "Empalme" en && objeto 'unirse' en objeto;
  },

  isHash: function (objeto) {
    regresar objeto Hash instanceof;
  },

  isFunction: function (objeto) {
    volver == typeof objeto "función";
  },

  isString: function (objeto) {
    regresar objeto typeof == "cadena";
  },

  ESNUMERO: function (objeto) {
    regresar objeto typeof == "número";
  },

  isUndefined: function (objeto) {
    regresar objeto typeof == "undefined";
  }
});

Object.extend (Function.prototype, {
  argumentNames: function () {
    var names = this.toString (). match (/ ^ [\ s \ (] * La función [^ (] * \ ((. *?) \) /) [1]. split (","). invoke ( "strip");
    volver names.length == 1 &&! nombres [0]? []: Nombres;
  },

  bind: function () {
    if (arguments.length <2 && Object.isUndefined (arguments [0])) devuelva este;
    var método __ = esta args, $ A = (argumentos), objeto args.shift = ();
    función de retorno () {
      retorno __ method.apply (objeto, args.concat ($ A (argumentos)));
    }
  },

  bindAsEventListener: function () {
    var método __ = esta args, $ A = (argumentos), objeto args.shift = ();
    retorno de la función (event) {
      retorno __ method.apply (objeto, [evento | | window.event] concat (args).);
    }
  },

  curry: function () {
    (! arguments.length) si devolver este;
    var __ method = esta args, $ A = (argumentos);
    función de retorno () {
      volver __ method.apply (esto, args.concat ($ A (argumentos)));
    }
  },

  demora: function () {
    var __ method = este, args = $ A (argumentos), timeout = args.shift () * 1000;
    volver window.setTimeout (function () {
      retorno __ method.apply (__method, args);
    }, Tiempo de espera);
  },

  wrap: function (wrapper) {
    var método __ = this;
    función de retorno () {
      volver wrapper.apply (esto, [__method.bind (this)] concat ($ A (argumentos)).);
    }
  },

  metodizar: function () {
    if (this._methodized) devuelven this._methodized;
    var método __ = this;
    devolver la función this._methodized = () {
      retorno __ method.apply (null, [esto] concat ($ A (argumentos)).);
    };
  }
});

Function.prototype.defer = Function.prototype.delay.curry (0,01);

Date.prototype.toJSON = function () {
  regresar '"' + this.getUTCFullYear () + '-' +
    . (This.getUTCMonth () + 1) toPaddedString (2) + '-' +
    this.getUTCDate (). toPaddedString (2) + 'T' +
    . this.getUTCHours () toPaddedString (2) + ":" +
    . this.getUTCMinutes () toPaddedString (2) + ":" +
    this.getUTCSeconds () toPaddedString (2) + 'Z' ".;
};

Try var = {
  siguientes: function () {
    var returnValue;

    for (var i = 0, longitud = arguments.length; longitud i <; i + +) {
      var lambda = argumentos [i];
      try {
        returnValue = lambda ();
        break;
      } Catch (e) {}
    }

    volver returnValue;
  }
};

RegExp.prototype.match = RegExp.prototype.test;

RegExp.escape = function (str) {
  retorno String (cadena) reemplazar. (/ ([* + ^ =:.? $ {} () | [\] \ / \ \]) / g, '\ \ $ 1');
};

/ * ------------------------------------------------ -------------------------- * /

var PeriodicalExecuter = Class.create ({
  initialize: function (devolución de llamada, frecuencia) {
    this.callback = callback;
    this.frequency = frecuencia;
    this.currentlyExecuting = false;

    this.registerCallback ();
  },

  RegisterCallback: function () {
    this.timer = setInterval (this.onTimerEvent.bind (este), this.frequency * 1000);
  },

  ejecute: function () {
    this.callback (this);
  },

  parada: function () {
    si regreso (this.timer!);
    clearInterval (this.timer);
    this.timer = null;
  },

  onTimerEvent: function () {
    if (! this.currentlyExecuting) {
      try {
        this.currentlyExecuting = true;
        this.execute ();
      Finally {}
        this.currentlyExecuting = false;
      }
    }
  }
});
Object.extend (String, {
  interpretar: function (value) {
    devolver el valor == null? '': String (value);
  },
  SpecialChar: {
    '\ B': '\ \ b',
    '\ T': '\ \ t',
    '\ N': '\ \ n',
    '\ F': '\ \ f',
    '\ R': '\ \ r',
    '\ \': '\ \ \ \'
  }
});

Object.extend (String.prototype, {
  gsub: function (patrón, reemplazamiento) {
    var resultado ='', source = esto, coinciden;
    reemplazo = arguments.callee.prepareReplacement (reemplazo);

    mientras que (source.length> 0) {
      si (partido source.match = (patrón)) {
        resultado + = source.slice (0, match.index);
        resultado + = String.interpret (reemplazo (el partido));
        fuente = source.slice (. match.index + Partido [0] de largo);
      } Else {
        resultado + = fuente, fuente ='';
      }
    }
    return resultado;
  },

  sub: function (patrón, reemplazamiento, count) {
    reemplazo = this.gsub.prepareReplacement (reemplazo);
    count = Object.isUndefined (cuenta)? 1: contar;

    volver this.gsub (patrón, function (partido) {
      si - partido de vuelta (recuento <0) [0];
      retorno de reemplazo (partido);
    });
  },

  escaneo: function (patrón, iterador) {
    this.gsub (patrón, repetidor);
    volver String (this);
  },

  truncate: function (longitud, el truncamiento) {
    longitud = longitud | | 30;
    truncamiento = Object.isUndefined (truncamiento)? '...' : Truncamiento;
    volver this.length> longitud?
      this.slice (0, length - truncation.length) + truncamiento: String (this);
  },

  tira: function () {
    volver this.replace (/ ^ \ s + /,'') replace (/ \ s + $ /,'').;
  },

  stripTags: function () {
    volver this.replace (<? \ / [^> /] +> / gi,'');
  },

  stripScripts: function () {
    volver this.replace (nuevo RegExp (Prototype.ScriptFragment, 'img'),'');
  },

  extractScripts: function () {
    var = new RegExp Matchall (Prototype.ScriptFragment, 'img');
    var = new RegExp matchOne (Prototype.ScriptFragment, 'im');
    return (this.match (Matchall) | | []). mapa (function (scriptTag) {
      return (scriptTag.match (matchOne) | | ['','']) [1];
    });
  },

  evalScripts: function () {
    this.extractScripts retorno () Mapa (function (script) {return eval (guión)}).;
  },

  escapeHTML: function () {
    var self = arguments.callee;
    self.text.data = this;
    volver self.div.innerHTML;
  },

  unescapeHTML: function () {
    var div = new Element ('div');
    div.innerHTML this.stripTags = ();
    div.childNodes retorno [0]? (Div.childNodes.length> 1?
      $ A (div.childNodes) inyectar ('', function (memo, nodo) {nota regreso + Node.nodeValue}).:
      div.childNodes [0] nodeValue):.'';
  },

  toQueryParams: function (separador) {
    var partido this.strip = () Partido (/ ([^ #?] *) (# *) $ /.?).;
    si el retorno {} (partido!);

    volver match [1] split. (separador | | '&') inyectar ({}, function (par hash) {.
      if ((par = pair.split ('=')) [0]) {
        var tecla = decodeURIComponent (pair.shift ());
        var = valor pair.length> 1? pair.join ('='): par [0];
        if (valor = undefined!) = valor decodeURIComponent (value);

        if (clave hash) {
          si hash [clave] = [hash [clave]] (Object.isArray (hash [clave])!);
          hash [clave] push (valor).;
        }
        otro hash [clave] = valor;
      }
      volver hash;
    });
  },

  toArray: function () {
    volver this.split ('');
  },

  succ: function () {
    volver this.slice (0, this.length - 1) +
      String.fromCharCode (this.charCodeAt (this.length - 1) + 1);
  },

  veces: function (count) {
    volver contador <1? '': New Array (cuenta + 1) join (this);.
  },

  camelize: function () {
    partes var = this.split ('-'), len parts.length =;
    if (len == 1) Piezas de retorno [0];

    var = camelized this.charAt (0) == '-'
      ? partes [0]. charAt (0). toUpperCase () + partes [0]. substring (1)
      : Partes [0];

    for (var i = 1; i <len, i + +)
      camelized + = partes [i] charAt (0) toUpperCase () + piezas [i] subcadena (1)...;

    volver camelized;
  },

  capitalizar: function () {
    volver this.charAt (0) toUpperCase () this.substring + (1) toLowerCase ()..;
  },

  subrayado: function () {
    volver this.gsub (/ :: /,
  },

  dasherize: function () {
    volver this.gsub (/ _ /, '-');
  },

  inspeccionar: function (useDoubleQuotes) {
    escapedString var = this.gsub (/ [\ x00-\ x1f \ \] /, function (partido) {
      var = carácter String.specialChar [Partido [0]];
      volver personaje? .. caracteres: "\ \ u00 '+ Partido [0] charCodeAt () toPaddedString (2, 16);
    });
    if (useDoubleQuotes) return '"' + escapedString.replace (/" / g, '\ \ "') + '"';
    return "'" + escapedString.replace (/' / g, '\ \ \'') + "'";
  },

  toJSON: function () {
    volver this.inspect (true);
  },

  unfilterJSON: function (filtro) {
    volver this.sub (filtro | | Prototype.JSONFilter, '# {1}');
  },

  isJSON: function () {
    var str = this;
    if (str.blank ()) return false;
    (. / \ \ / g, '@'). this.replace str = replace (/ "[^" \ \ \ n \ r] * "/ g,'');
    . retorno (/ ^ [,.: {} \ [\] 0-9 \ - + Eaeflnr-u \ n \ r \ t] * $ /) prueba (str);
  },

  evalJSON: function (desinfectar) {
    var json = this.unfilterJSON ();
    try {
      si | volver eval ('(' + json + ')') (desinfectar | json.isJSON ()!);
    } Catch (e) {}
    throw new SyntaxError ('Mal formado JSON cadena:' + this.inspect ());
  },

  incluyen: function (patrón) {
    volver this.indexOf (patrón)> -1;
  },

  startsWith: function (patrón) {
    volver this.indexOf (patrón) === 0;
  },

  endsWith: function (patrón) {
    var d = this.length - pattern.length;
    volver d> = 0 && this.lastIndexOf (patrón) === d;
  },

  function () {: vacío
    devolver esta =='';
  },

  blanco: function () {
    . volver / ^ \ s * $ / test (this);
  },

  interpolar: function (objeto, patrón) {
    volver plantilla nueva (este patrón,) evaluar (objeto).;
  }
});

if (Prototype.Browser.WebKit | | Prototype.Browser.IE) Object.extend (String.prototype, {
  escapeHTML: function () {
    . volver this.replace (/ & / g, '&') replace (/ <. / g, '<') reemplazar (/> / g, '>');
  },
  unescapeHTML: function () {
    . volver this.replace (/ & / g, '&') replace (/ <. / g, '<') reemplazar (/> / g, '>');
  }
});

String.prototype.gsub.prepareReplacement = function (reemplazo) {
  if (Object.isFunction (reemplazo)) reemplazo de retorno;
  var template = Plantilla de nuevo (sustitución);
  función de retorno (partido) {return template.evaluate (partido)};
};

String.prototype.parseQuery = String.prototype.toQueryParams;

Object.extend (String.prototype.escapeHTML, {
  div: document.createElement ('div'),
  texto: document.createTextNode ('')
});

con (String.prototype.escapeHTML) div.appendChild (texto);

var = Class.create plantilla ({
  initialize: function (plantilla, patrón) {
    this.template template.toString = ();
    this.pattern patrón = | | Template.Pattern;
  },

  evaluar: function (objeto) {
    if (Object.isFunction (object.toTemplateReplacements))
      object.toTemplateReplacements objeto = ();

    volver this.template.gsub (this.pattern, function (partido) {
      if (objeto == null) return'';

      var before = Partido [1] | |'';
      if (antes == '\ \') partido de vuelta [2];

      var ctx = objeto, expr = Partido [3];
      var pattern = / ^ ([^ [] + | \ [((:.?.? * [^ \ \])) \]) (. \ | \ [| $) /;
      match = pattern.exec (expr);
      si (partido == null) return antes;

      while (partido! = null) {
        var comp = match [1]. startsWith ('[')? match [2] gsub ('\ \ \ \]', ']'):. match [1];
        ctx = ctx [comp];
        if (null == ctx | |'' == match [3]) break;
        expr = expr.substring ('[' == match [3] match [1] Longitud:.. match [0] de largo);
        match = pattern.exec (expr);
      }

      antes de volver + String.interpret (ctx);
    });
  }
});
Template.Pattern = / (^ | | \ r | \ n.) (# \ {(*) \.?}) /;

var $ rotura = {};

var = {Enumerable
  cada uno: function (iterator, context) {
    var index = 0;
    iterador = iterator.bind (contexto);
    try {
      this._each (function (value) {
        iterador (valor, índice + +);
      });
    Captura} (e) {
      if (! $ e = descanso) throw e;
    }
    devolver este;
  },

  eachSlice: function (número, iterator, context) {
    iterador iterador =? iterator.bind (contexto): Prototype.K;
    var index = número-, rebanadas = [] = array this.toArray ();
    while (índice (+ = número) <array.length)
      slices.push (Array.slice (index, índice de número +));
    volver slices.collect (iterador, contexto);
  },

  todo: function (iterator, context) {
    iterador iterador =? iterator.bind (contexto): Prototype.K;
    var resultado = true;
    this.each (function (valor, index) {
      resultado = && resultado iterador (valor, índice)!;
      (! resultado) si tirar $ break;
    });
    return resultado;
  },

  cualquiera: function (iterator, context) {
    iterador iterador =? iterator.bind (contexto): Prototype.K;
    var resultado = false;
    this.each (function (valor, index) {
      if (resultado =! iterador (valor, índice))
        throw $ break;
    });
    return resultado;
  },

  recoger: function (iterator, context) {
    iterador iterador =? iterator.bind (contexto): Prototype.K;
    resultados var = [];
    this.each (function (valor, index) {
      results.push (iterador (valor, índice));
    });
    devuelven resultados;
  },

  detectar: ​​function (iterator, context) {
    iterador = iterator.bind (contexto);
    var result;
    this.each (function (valor, index) {
      if (iterador (valor, índice)) {
        resultado = valor;
        throw $ break;
      }
    });
    return resultado;
  },

  findAll: function (iterator, context) {
    iterador = iterator.bind (contexto);
    resultados var = [];
    this.each (function (valor, index) {
      if (iterador (valor, índice))
        results.push (value);
    });
    devuelven resultados;
  },

  grep: function (filtro, iterator, context) {
    iterador iterador =? iterator.bind (contexto): Prototype.K;
    resultados var = [];

    if (Object.isString (filtro))
      filtro = new RegExp (filtro);

    this.each (function (valor, index) {
      if (filter.match (valor))
        results.push (iterador (valor, índice));
    });
    devuelven resultados;
  },

  incluyen: function (objeto) {
    if (Object.isFunction (this.indexOf))
      if (this.indexOf (objeto) = -1!) return true;

    var encontrado = false;
    this.each (function (value) {
      if (value == objeto) {
        encontrado = true;
        throw $ break;
      }
    });
    volver encontrado;
  },

  inGroupsOf: function (número, fillWith) {
    fillWith = Object.isUndefined (fillWith)? null: fillWith;
    volver this.eachSlice (número, función (rebanada) {
      while (slice.length <numero) slice.push (fillWith);
      volver rebanada;
    });
  },

  inyectar: ​​function (memo, iterator, context) {
    iterador = iterator.bind (contexto);
    this.each (function (valor, index) {
      memo iterador = (memo, valor, índice);
    });
    volver memo;
  },

  invocar: function (método) {
    var args = $ A (argumentos) slice (1).;
    volver this.map (function (value) {
      valor de retorno [método] se aplican (valor, args).;
    });
  },

  max: function (iterator, context) {
    iterador iterador =? iterator.bind (contexto): Prototype.K;
    var result;
    this.each (function (valor, index) {
      valor iterador = (valor, índice);
      if (resultado == null | | valor> = resultado)
        resultado = valor;
    });
    return resultado;
  },

  min: function (iterator, context) {
    iterador iterador =? iterator.bind (contexto): Prototype.K;
    var result;
    this.each (function (valor, index) {
      valor iterador = (valor, índice);
      if (resultado == null | | valor del resultado <)
        resultado = valor;
    });
    return resultado;
  },

  partición: function (iterator, context) {
    iterador iterador =? iterator.bind (contexto): Prototype.K;
    var TRUE = [], falses = [];
    this.each (function (valor, index) {
      (Iterador (valor, índice)?
        TRUE:. falses) push (valor);
    });
    retorno [TRUE, falses];
  },

  desplumar: function (propiedad) {
    resultados var = [];
    this.each (function (value) {
      results.push (valor [de la propiedad]);
    });
    devuelven resultados;
  },

  rechazar: function (iterator, context) {
    iterador = iterator.bind (contexto);
    resultados var = [];
    this.each (function (valor, index) {
      if (! iterador (valor, índice))
        results.push (value);
    });
    devuelven resultados;
  },

  sortBy: function (iterator, context) {
    iterador = iterator.bind (contexto);
    volver this.map (function (valor, index) {
      volver {valor: valor, criterios: iterador (valor, índice)};
    }). Clase (function (izquierda, derecha) {
      var un left.criteria =, b = right.criteria;
      volver a b <? -1: A> b? 1: 0;
    .}) Arrancar ('valor');
  },

  toArray: function () {
    volver this.map ();
  },

  zip: function () {
    var iterador = Prototype.K, args = $ A (argumentos);
    if (Object.isFunction (args.last ()))
      iterador = args.pop ();

    .. colecciones var = [esto] concat (args) map ($ A);
    volver this.map (function (valor, index) {
      volver iterador (collections.pluck (índice));
    });
  },

  tamaño: function () {
    volver this.toArray () de longitud.;
  },

  inspeccionar: function () {
    volver '# <Enumerable:' + + this.toArray().inspect()'>';
  }
};

Object.extend (Enumerable, {
  Mapa: Enumerable.collect,
  encontrar: Enumerable.detect,
  seleccione: Enumerable.findAll,
  Filtro: Enumerable.findAll,
  miembro: Enumerable.include,
  Partidas: Enumerable.toArray,
  cada: Enumerable.all,
  algunos: Enumerable.Any
});
función $ A (iterable) {
  si es una declaración [] (iterable!);
  if (iterable.toArray) return iterable.toArray ();
  var = longitud iterable.length | | 0, resultados = new Array (longitud);
  while (longitud -) resultados [longitud] = [longitud] iterable;
  devuelven resultados;
}

si (Prototype.Browser.WebKit) {
  $ A = function (iterable) {
    si es una declaración [] (iterable!);
    if (! (Object.isFunction (iterable) && iterables == '[objeto NodeList]') &&
        iterable.toArray) return iterable.toArray ();
    var = longitud iterable.length | | 0, resultados = new Array (longitud);
    while (longitud -) resultados [longitud] = [longitud] iterable;
    devuelven resultados;
  };
}

Array.from = $ A;

Object.extend (Array.prototype, Enumerable);

si Array.prototype._reverse = Array.prototype.reverse (Array.prototype._reverse!);

Object.extend (Array.prototype, {
  _each: function (repetidor) {
    for (var i = 0, longitud = this.length; longitud i <; i + +)
      iterador (esta [i]);
  },

  claro: function () {
    this.length = 0;
    devolver este;
  },

  primero: function () {
    devuelva este [0];
  },

  pasado: function () {
    devuelva este [this.length - 1];
  },

  compacto: function () {
    volver this.select (function (value) {
      valor devuelto = null!;
    });
  },

  aplanar: function () {
    volver this.inject ([], la función (array, value) {
      volver Array.concat (Object.isArray (valor)?
        value.flatten (): [valor]);
    });
  },

  sin: function () {
    valores var = $ A (argumentos);
    volver this.select (function (value) {
      volver values.include (valor)!;
    });
  },

  inversa: function (en línea) {
    retorno (línea == false esto: this.toArray ()?) _reverse ();.
  },

  reducir: function () {
    volver this.length> 1? : este [0];
  },

  uniq: function (ordenado) {
    volver this.inject ([], la función (array, valor, index) {
      if (0 == índice | | (ordenados array.last () = valor:?! array.include (valor)))
        Array.push (value);
      volver array;
    });
  },

  se cruzan: function (array) {
    volver this.uniq (). findAll (function (item) {
      volver array.detect (function (value) {item} === valor de retorno);
    });
  },

  clon: function () {
    volver [] concat (this).;
  },

  tamaño: function () {
    volver this.length;
  },

  inspeccionar: function () {
    return '[' + this.map (Object.inspect) join (',') +. ']';
  },

  toJSON: function () {
    resultados var = [];
    this.each (function (objeto) {
      var = valor Object.toJSON (objeto);
      si results.push (valor) (Object.isUndefined (valor)!);
    });
    return '[' + results.join (',') + ']';
  }
});

/ / Usar navegador nativo JS 1,6 implementación si está disponible
if (Object.isFunction (Array.prototype.forEach))
  Array.prototype._each = Array.prototype.forEach;

if (! Array.prototype.indexOf) Array.prototype.indexOf = function (elemento, i) {
  i | | (i = 0);
  var = longitud this.length;
  if (i <0) i = largo + i;
  for (; i longitud <; i + +)
    if (esto [i] === elemento) i volver;
  devuelve -1;
};

if (! Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function (elemento, i) {
  i = isNaN (i)? this.length: (¿i <0 this.length + i: i) + 1;
  . this.slice var n = (0, i) reverse () indexOf (elemento).;
  volver (n <0)? n: i - n - 1;
};

Array.prototype.toArray = Array.prototype.clone;

función $ w (cadena) {
  si es una declaración [] (Object.isString (cadena)!);
  cadena = string.strip ();
  volver cuerda? string.split (/ \ s + /): [];
}

si (Prototype.Browser.Opera) {
  Array.prototype.concat = function () {
    var array = [];
    for (var i = 0, longitud = this.length; i <longitud, i + +) Array.push (esto [i]);
    for (var i = 0, longitud = arguments.length; longitud i <; i + +) {
      if (Object.isArray (arguments [i])) {
        for (var j = 0, = arrayLength argumentos [i] longitud;. j <arrayLength, j + +)
          Array.push (arguments [i] [j]);
      } Else {
        Array.push (arguments [i]);
      }
    }
    volver array;
  };
}
Object.extend (Number.prototype, {
  toColorPart: function () {
    volver this.toPaddedString (2, 16);
  },

  succ: function () {
    devuelva este + 1;
  },

  veces: function (repetidor) {
    $ R (0, este, true) cada uno (repetidor).;
    devolver este;
  },

  toPaddedString: function (longitud, radix) {
    var cadena = this.toString (radix | | 10);
    . volver '0 'veces (longitud - string.length) + cadena;
  },

  toJSON: function () {
    volver isFinite (este)? this.toString (): 'null';
  }
});

$ W ('round abs piso techo "). Cada una (función (método) {
  Number.prototype [método] = Math [método] metodizar ().;
});
función $ H (objeto) {
  volver Hash nueva (objeto);
};

var hash = Class.create (Enumerable, (function () {

  función toQueryPair (clave, valor) {
    if (Object.isUndefined (valor)) tecla de retorno;
    regresar clave + '=' + encodeURIComponent (String.interpret (valor));
  }

  volver {
    initialize: function (objeto) {
      this._object = Object.isHash (objeto)? object.toObject (): Object.clone (objeto);
    },

    _each: function (repetidor) {
      DE (clave var en this._object) {
        var = valor this._object [clave], par = [valor llave,];
        pair.key = tecla;
        pair.value = valor;
        iterador (par);
      }
    },

    set: function (clave, valor) {
      volver this._object [clave] = valor;
    },

    obtener: function (key) {
      volver this._object [clave];
    },

    unset: function (key) {
      var = valor this._object [clave];
      delete this._object [clave];
      valor de retorno;
    },

    toObject: function () {
      volver Object.clone (this._object);
    },

    claves: function () {
      volver this.pluck ('clave');
    },

    valores: function () {
      volver this.pluck ('valor');
    },

    índice: function (value) {
      var partido this.detect = (function (par) {
        volver pair.value valor ===;
      });
      volver coincidir && match.key;
    },

    fusión: function (objeto) {
      volver this.clone () actualización (objeto).;
    },

    actualización: function (objeto) {
      volver Hash nuevo (objeto). inyección (esta función (resultado, par) {
        result.set (pair.key, pair.value);
        return resultado;
      });
    },

    toQueryString: function () {
      volver this.map (function (par) {
        var tecla = encodeURIComponent (pair.key), los valores = pair.value;

        if (typeof && valores valores == "objeto") {
          if (Object.isArray (valores))
            volver values.map (toQueryPair.curry (key)) join ('&').;
        }
        volver toQueryPair (valores clave,);
      }) Join ('&').;
    },

    inspeccionar: function () {
      volver '# <Hash: {' + this.map función ((par) {
        volver pair.map (Object.inspect) join (':').;
      }) Join (',') + '}>'.;
    },

    toJSON: function () {
      volver Object.toJSON (this.toObject ());
    },

    clon: function () {
      volver Hash nuevo (this);
    }
  }
}) ());

Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
Hash.from = $ H;
var ObjectRange Class.create = ({Enumerable,
  initialize: function (inicio, final y exclusivo) {
    this.start = inicio;
    this.end = fin;
    this.exclusive = exclusiva;
  },

  _each: function (repetidor) {
    var = valor this.start;
    while (this.include (valor)) {
      iterador (valor);
      valor = value.succ ();
    }
  },

  incluyen: function (value) {
    if (valor <this.start)
      return false;
    if (this.exclusive)
      valor de retorno <this.end;
    valor de retorno <= this.end;
  }
});

var $ R = function (inicio, final y exclusivo) {
  return new ObjectRange (inicio, final y exclusivo);
};

var Ajax = {
  getTransport: function () {
    volver Try.these (
      function () {return new XMLHttpRequest ()},
      function () {return new ActiveXObject ('Msxml2.XMLHTTP')},
      function () {return new ActiveXObject ('Microsoft.XMLHTTP')}
    ) | | False;
  },

  activeRequestCount: 0
};

Ajax.Responders = {
  respondedores: [],

  _each: function (repetidor) {
    this.responders._each (repetidor);
  },

  Registro: function (respuesta) {
    if (! this.include (respondedor))
      this.responders.push (respuesta);
  },

  unregister: function (respuesta) {
    this.responders = this.responders.without (respuesta);
  },

  despacho: function (devolución de llamada, petición, el transporte, json) {
    this.each (function (respuesta) {
      if (Object.isFunction (respondedor [callback])) {
        try {
          respondedor [callback] se aplican (respondedor, [petición, el transporte, json]).;
        } Catch (e) {}
      }
    });
  }
};

Object.extend (Ajax.Responders, Enumerable);

Ajax.Responders.register ({
  onCreate: function () {+ +} Ajax.activeRequestCount,
  onComplete: function () {Ajax.activeRequestCount--}
});

Ajax.Base = Class.create ({
  initialize: function (options) {
    this.options = {
      método: 'post',
      asíncrona: true,
      contentType: "application / x-www-form-urlencoded ',
      encoding: 'UTF-8',
      Parámetros:'',
      evalJSON: true,
      evalJS: true
    };
    Object.extend (this.options, opciones | | {});

    this.options.method this.options.method.toLowerCase = ();

    if (Object.isString (this.options.parameters))
      this.options.parameters this.options.parameters.toQueryParams = ();
    else if (Object.isHash (this.options.parameters))
      this.options.parameters this.options.parameters.toObject = ();
  }
});

Ajax.Request = Class.create (Ajax.Base, {
  _complete: false,

  initialize: function ($ super, url, options) {
    $ Super (opciones);
    this.transport Ajax.getTransport = ();
    this.request (url);
  },

  petición: function (url) {
    this.url = url;
    this.method = this.options.method;
    var params = Object.clone (this.options.parameters);

    if (! ['get', 'post']. incluyen (this.method)) {
      / / Simular otros verbos en el poste
      params ['_method'] = this.method;
      this.method = 'mensaje';
    }

    this.parameters = params;

    if (params = Object.toQueryString (params)) {
      / / Cuando GET, añadir parámetros a la URL
      if (this.method == 'get')
        this.url + = (this.url.include () '&' '?': '?') + params;
      else if (/ Konqueror | Safari | KHTML / .test (navigator.userAgent))
        params + = '& _ =';
    }

    try {
      var respuesta = new Ajax.Response (this);
      if (this.options.onCreate) this.options.onCreate (respuesta);
      Ajax.Responders.dispatch ('onCreate ", esta respuesta);

      this.transport.open (this.method.toUpperCase (), this.url,
        this.options.asynchronous);

      si (this.options.asynchronous) this.respondToReadyState.bind (este) aplazar (1).;

      this.transport.onreadystatechange = this.onStateChange.bind (this);
      this.setRequestHeaders ();

      this.body = this.method == 'post'? (This.options.postBody | | params): null;
      this.transport.send (this.body);

      / * Fuerza de Firefox para manejar 4 estado listo para solicitudes sincrónicas * /
      if (! this.options.asynchronous this.transport.overrideMimeType &&)
        this.onStateChange ();

    }
    catch (e) {
      this.dispatchException (e);
    }
  },

  onStateChange: function () {
    var = readyState this.transport.readyState;
    if (readyState> 1 &&! ((readyState == 4) && this._complete))
      this.respondToReadyState (this.transport.readyState);
  },

  setRequestHeaders: function () {
    cabeceras var = {
      'X-Requested-With': 'XMLHttpRequest',
      'X-Prototype-Version': Prototype.Version,
      'Aceptar': 'text / javascript, text / html, application / xml, text / xml, * / *'
    };

    if (this.method == 'post') {
      headers ['Content-type'] = this.options.contentType +
        (This.options.encoding '; charset =' + this.options.encoding:'');

      / Force * "Connection: close" para exploradores más antiguos de Mozilla para trabajar
       * En torno a un error por el que XMLHttpRequest envía una incorrecta
       * Encabezado Content-Length. Ver Bugzilla # 246651 Mozilla.
       * /
      if (&& this.transport.overrideMimeType
          (Navigator.userAgent.match (/ Gecko \ / (\ d {4}) /) | | [0,2005]) [1] <2005)
            cabeceras ['Conexión'] = 'Cerrar';
    }

    / / Definido por el usuario encabezados
    if (typeof this.options.requestHeaders == 'objeto') {
      var = this.options.requestHeaders extras;

      if (Object.isFunction (extras.push))
        for (var i = 0, longitud = extras.length; longitud i <; i + = 2)
          encabezados [extras [i]] = extras [i +1];
      más
        $ H (extras) cada uno (function (par) {cabeceras [pair.key] = pair.value}).;
    }

    para (nombre var en cabeceras)
      this.transport.setRequestHeader (nombre, cabeceras [nombre]);
  },

  éxito: function () {
    var estado = this.getStatus ();
    devolver el estado | | (estado> = 200 && estado <300)!;
  },

  getStatus: function () {
    try {
      volver this.transport.status | | 0;
    } Catch (e) {return 0}
  },

  respondToReadyState: function (readyState) {
    var state = Ajax.Request.Events [readyState], respuesta = new Ajax.Response (this);

    if (estado == 'Completo') {
      try {
        this._complete = true;
        (This.options ['a' + Response.Status]
         | | This.options ['on' + (? This.success () 'Success': 'No')]
         | | Prototype.emptyFunction) (respuesta, response.headerJSON);
      Captura} (e) {
        this.dispatchException (e);
      }

      var contentType response.getHeader = ('Content-type');
      if (this.options.evalJS == 'fuerza'
          | | (This.options.evalJS this.isSameOrigin && () && contentType
          ContentType.match && (/ ^ \ s * texto (| solicitud) \ / (x-) (java | ECMA) guión (;?.? *) \ S * $ / i)))
        this.evalResponse ();
    }

    try {
      (This.options ['a' + estado] | | Prototype.emptyFunction) (respuesta, response.headerJSON);
      Ajax.Responders.dispatch (estado "on" +, esta respuesta, response.headerJSON);
    Captura} (e) {
      this.dispatchException (e);
    }

    if (estado == 'Completo') {
      / / Evitar la pérdida de memoria en MSIE: limpiar
      this.transport.onreadystatechange = Prototype.emptyFunction;
    }
  },

  isSameOrigin: function () {
    var m = this.url.match (/ ^ \ s * https: \ / \ / [^ \ /] * /);
    regresar m | |. (m [0] == '# {protocolo} / / # {dominio} # {port}' interpolar ({
      protocolo: location.protocol,
      domain: document.domain,
      puerto: location.port? ':' + Location.port:''
    }));
  },

  getHeader: function (nombre) {
    try {
      volver this.transport.getResponseHeader (nombre) | | null;
    } Catch (e) {return null}
  },

  evalResponse: function () {
    try {
      volver eval ((this.transport.responseText | |'') unfilterJSON ().);
    Captura} (e) {
      this.dispatchException (e);
    }
  },

  dispatchException: function (excepción) {
    (This.options.onException | | Prototype.emptyFunction) (esta excepción,);
    Ajax.Responders.dispatch ('onException', esta excepción);
  }
});

Ajax.Request.Events =
  ['Inicializar', 'Cargar', 'Loaded', 'interactivo', 'Completa'];

Ajax.Response = Class.create ({
  initialize: function (peticion) {
    this.request = solicitud;
    var transport = this.transport = request.transport,
        readyState = this.readyState = transport.readyState;

    if ((readyState> 2 && Prototype.Browser.IE) | | readyState == 4) {
      this.status this.getStatus = ();
      this.statusText this.getStatusText = ();
      this.responseText = String.interpret (transport.responseText);
      this.headerJSON this._getHeaderJSON = ();
    }

    if (readyState == 4) {
      var xml = transport.responseXML;
      this.responseXML = Object.isUndefined (xml)? null: xml;
      this.responseJSON this._getResponseJSON = ();
    }
  },

  estado: 0,
  statusText:'',

  getStatus: Ajax.Request.prototype.getStatus,

  getStatusText: function () {
    try {
      volver this.transport.statusText | |'';
    } Catch (e) {return''}
  },

  getHeader: Ajax.Request.prototype.getHeader,

  getallheaders: function () {
    try {
      this.getAllResponseHeaders retorno ();
    } Catch (e) {return null}
  },

  getResponseHeader: function (nombre) {
    volver this.transport.getResponseHeader (nombre);
  },

  getAllResponseHeaders: function () {
    this.transport.getAllResponseHeaders retorno ();
  },

  _getHeaderJSON: function () {
    var json = this.getHeader ('X-JSON');
    si return null (json!);
    json = decodeURIComponent (escape (JSON));
    try {
      volver json.evalJSON (this.request.options.sanitizeJSON | |
        ! This.request.isSameOrigin ());
    Captura} (e) {
      this.request.dispatchException (e);
    }
  },

  _getResponseJSON: function () {
    var opciones = this.request.options;
    if (options.evalJSON | |! (options.evalJSON = 'fuerza' &&
      ! (This.getHeader ('Content-type') | |''). Include ('application / json')) | |
        this.responseText.blank ())
          return null;
    try {
      volver this.responseText.evalJSON (options.sanitizeJSON | |
        ! This.request.isSameOrigin ());
    Captura} (e) {
      this.request.dispatchException (e);
    }
  }
});

Ajax.Updater = Class.create (Ajax.Request, {
  initialize: function ($ super, contenedor, url, options) {
    this.container = {
      éxito: (container.success | | contenedor),
      fracaso: (container.failure | | (container.success nulo: contenedor))
    };

    options = Object.clone (opciones);
    var = onComplete options.onComplete;
    options.onComplete = (function (respuesta json) {
      this.updateContent (response.responseText);
      if (Object.isFunction (onComplete)) onComplete (respuesta, json);
    }) Se unen (this).;

    $ Super (url, opciones);
  },

  updateContent: function (responseText) {
    var = receptor this.container [this.success ()? 'Éxito': 'fracaso'],
        options = this.options;

    si responseText responseText.stripScripts = () (options.evalScripts!);

    if (receptor = $ (receptor)) {
      si (options.insertion) {
        if (Object.isString (options.insertion)) {
          var inserción = {}; inserción [options.insertion] = responseText;
          receiver.insert (inserción);
        }
        más options.insertion (receptor, responseText);
      }
      más receiver.update (responseText);
    }
  }
});

Ajax.PeriodicalUpdater = Class.create (Ajax.Base, {
  initialize: function ($ super, contenedor, url, options) {
    $ Super (opciones);
    this.onComplete = this.options.onComplete;

    this.frequency = (this.options.frequency | | 2);
    this.decay = (this.options.decay | | 1);

    this.updater = {};
    this.container = contenedor;
    this.url = url;

    this.start ();
  },

  start: function () {
    this.options.onComplete = this.updateComplete.bind (this);
    this.onTimerEvent ();
  },

  parada: function () {
    this.updater.options.onComplete = undefined;
    clearTimeout (this.timer);
    . (This.onComplete | | Prototype.emptyFunction) se aplican (esto, argumentos);
  },

  updateComplete: function (response) {
    si (this.options.decay) {
      this.decay = (response.responseText == this.lastText?
        this.decay * this.options.decay: 1);

      this.lastText = response.responseText;
    }
    this.timer = this.onTimerEvent.bind (this) delay (this.decay * this.frequency).;
  },

  onTimerEvent: function () {
    this.updater = new Ajax.Updater (this.container, this.url, this.options);
  }
});
función $ (elemento) {
  if (arguments.length> 1) {
    for (var i = 0, elementos = [], longitud = arguments.length; longitud i <; i + +)
      elements.push ($ (argumentos [i]));
    devolver elementos;
  }
  if (Object.isString (elemento))
    elemento = document.getElementById (elemento);
  volver Element.extend (elemento);
}

si (Prototype.BrowserFeatures.XPath) {
  document._getElementsByXPath = function (expresión, parentElement) {
    resultados var = [];
    var query = document.evaluate (expresión, $ (parentElement) | | documento,
      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    for (var i = 0, longitud = query.snapshotLength; longitud i <; i + +)
      results.push (Element.extend (query.snapshotItem (i)));
    devuelven resultados;
  };
}

/ * ------------------------------------------------ -------------------------- * /

si var nodo = {} (window.Node!);

if (! Node.ELEMENT_NODE) ​​{
  / / DOM nivel 2 ECMAScript Language Binding
  Object.extend (nodo, {
    ELEMENT_NODE: 1,
    ATTRIBUTE_NODE: 2,
    TEXT_NODE: 3,
    CDATA_SECTION_NODE: 4,
    ENTITY_REFERENCE_NODE: 5,
    ENTITY_NODE: 6,
    PROCESSING_INSTRUCTION_NODE: 7,
    COMMENT_NODE: 8,
    DOCUMENT_NODE: 9,
    DOCUMENT_TYPE_NODE: 10,
    DOCUMENT_FRAGMENT_NODE: 11,
    NOTATION_NODE: 12
  });
}

(Function () {
  var elemento = this.Element;
  this.Element = function (tagName, atributos) {
    atributos atributos = | | {};
    tagName tagName.toLowerCase = ();
    var cache = Element.cache;
    if (Prototype.Browser.IE attributes.name &&) {
      tagName = '<' + tagName + 'name = "' + attributes.name + '">';
      eliminar attributes.name;
      volver Element.writeAttribute (document.createElement (tagName), atributos);
    }
    si cache [tagName] = Element.extend (document.createElement (tagName)) (cache [tagName]!);
    volver Element.writeAttribute (Copia [] tagName cloneNode (false), atributos.);
  };
  Object.extend (this.Element, elemento | | {});
.}) Llame al (ventana);

Element.cache = {};

Element.Methods = {
  visible: function (elemento) {
    return $ (elemento) style.display = 'none'.!;
  },

  toggle: function (elemento) {
    elemento = $ (elemento);
    Elemento [Element.visible (elemento)? 'Ocultar': 'show'] (elemento);
    volver elemento;
  },

  hide: function (elemento) {
    $ (Elemento) style.display = 'none'.;
    volver elemento;
  },

  espectáculo: function (elemento) {
    $ (Elemento) style.display =''.;
    volver elemento;
  },

  eliminar: function (elemento) {
    elemento = $ (elemento);
    element.parentNode.removeChild (elemento);
    volver elemento;
  },

  actualización: function (elemento, contenido) {
    elemento = $ (elemento);
    if (contenido content.toElement &&) contenido content.toElement = ();
    if (Object.isElement (contenido)) return element.update () insert (contenido).;
    content = Object.toHTML (contenido);
    element.innerHTML content.stripScripts = ();
    . content.evalScripts.bind (contenido) aplazar ();
    volver elemento;
  },

  replace: function (elemento, contenido) {
    elemento = $ (elemento);
    if (contenido content.toElement &&) contenido content.toElement = ();
    else if (! Object.isElement (contenido)) {
      content = Object.toHTML (contenido);
      var rango = element.ownerDocument.createRange ();
      range.selectNode (elemento);
      . content.evalScripts.bind (contenido) aplazar ();
      content = range.createContextualFragment content.stripScripts (());
    }
    element.parentNode.replaceChild (contenido, elemento);
    volver elemento;
  },

  insert: function (elemento, inserciones) {
    elemento = $ (elemento);

    if (Object.isString (inserciones) | | Object.isNumber (inserciones) | |
        Object.isElement (inserciones) | | (inserciones && (insertions.toElement | | insertions.toHTML)))
          inserciones = {fondo: inserciones};

    var contenido, insertar, tagName, childNodes;

    para (posición var en inserciones) {
      content = inserciones [posición];
      = posición position.toLowerCase ();
      insertar = Element._insertionTranslations [posición];

      if (contenido content.toElement &&) contenido content.toElement = ();
      si (Object.isElement (contenido)) {
        insertar (elemento, contenido);
        continuar;
      }

      content = Object.toHTML (contenido);

      tagName = ((posición == 'antes' | | posición == 'después')
        ? . element.parentNode: Elemento) tagName.toUpperCase ();

      childNodes = Element._getContentFromAnonymousElement (tagName, content.stripScripts ());

      if (posición == 'top' | | posición == 'después') childNodes.reverse ();
      childNodes.each (insert.curry (elemento));

      . content.evalScripts.bind (contenido) aplazar ();
    }

    volver elemento;
  },

  wrap: function (elemento, envoltura, atributos) {
    elemento = $ (elemento);
    if (Object.isElement (wrapper))
      $ (Wrapper) WriteAttribute (atributos | | {}).;
    else if (Object.isString (wrapper)) wrapper = new Element (wrapper, atributos);
    envoltura más = new Element ('div', envoltura);
    if (element.parentNode)
      element.parentNode.replaceChild (contenedor, elemento);
    wrapper.appendChild (elemento);
    volver envoltorio;
  },

  inspeccionar: function (elemento) {
    elemento = $ (elemento);
    var resultado = '<' + element.tagName.toLowerCase ();
    $ H ({'id': 'id', 'className': 'clase'}). Cada uno (function (par) {
      var property = pair.first (), atributo = pair.last ();
      var = valor (elemento [propiedad] | |'') toString ().;
      if (value) = resultado + '' + atributo + '=' + value.inspect (true);
    });
    return resultado + '>';
  },

  recursivelyCollect: function (elemento, la propiedad) {
    elemento = $ (elemento);
    elementos var = [];
    while (elemento = elemento [propiedad])
      si (element.nodeType == 1)
        elements.push (Element.extend (elemento));
    devolver elementos;
  },

  antepasados: función (elemento) {
    volver recursivelyCollect $ (elemento) ('parentNode.')
  },

  descendientes: función (elemento) {
    return $ (elemento) seleccionado ("*").;
  },

  firstDescendant: function (elemento) {
    elemento = $ (elemento) firstChild.;
    while (&& elemento element.nodeType = 1!) = elemento element.nextSibling;
    return $ (elemento);
  },

  immediateDescendants: función (elemento) {
    si es una declaración [] (elemento (= $ (elemento) firstChild)!.);
    while (&& elemento element.nodeType = 1!) = elemento element.nextSibling;
    if (elemento) return [elemento] concat ($ (elemento) nextSiblings ().).;
    volver [];
  },

  previousSiblings: función (elemento) {
    volver recursivelyCollect $ (elemento) ('previousSibling.')
  },

  nextSiblings: función (elemento) {
    volver recursivelyCollect $ (elemento) ('nextSibling.')
  },

  hermanos: función (elemento) {
    elemento = $ (elemento);
    . element.previousSiblings Volver () reverse () concat (element.nextSiblings ()).;
  },

  partido: function (elemento, selector) {
    if (Object.isString (selector))
      selector = new Selector (selector);
    volver selector.match ($ (elemento));
  },

  arriba: function (elemento, expresión, index) {
    elemento = $ (elemento);
    if (arguments.length == 1) return $ (element.parentNode);
    antepasados ​​var = element.ancestors ();
    volver Object.isNumber (expresión)? antepasados ​​[expresión]:
      Selector.findElement (ancestros, expresión, índice);
  },

  abajo: function (elemento, expresión, index) {
    elemento = $ (elemento);
    if (arguments.length == 1) retorno element.firstDescendant ();
    volver Object.isNumber (expresión)? element.descendants () [expresión]:
      Element.select (expresión) [Índice | | 0];
  },

  anterior: function (elemento, expresión, index) {
    elemento = $ (elemento);
    if (arguments.length == 1) return $ (Selector.handlers.previousElementSibling (elemento));
    previousSiblings var = element.previousSiblings ();
    volver Object.isNumber (expresión)? previousSiblings [expresión]:
      Selector.findElement (previousSiblings, expresión, índice);
  },

  siguiente: function (elemento, expresión, index) {
    elemento = $ (elemento);
    if (arguments.length == 1) return $ (Selector.handlers.nextElementSibling (elemento));
    nextSiblings var = element.nextSiblings ();
    volver Object.isNumber (expresión)? nextSiblings [expresión]:
      Selector.findElement (nextSiblings, expresión, índice);
  },

  seleccionar: function () {
    var args = $ A (argumentos), elemento = $ (args.shift ());
    volver Selector.findChildElements (elemento, args);
  },

  function () {: adyacente
    var args = $ A (argumentos), elemento = $ (args.shift ());
    volver Selector.findChildElements (element.parentNode, args) sin (elemento).;
  },

  identificar: function (elemento) {
    elemento = $ (elemento);
    var id = element.readAttribute ('id'), self = arguments.callee;
    if (id) Identificación del retorno;
    do {id = 'anonymous_element_' + self.counter + +} mientras que ($ (id));
    element.writeAttribute ('id', id);
    volver id;
  },

  readAttribute: function (elemento, nombre) {
    elemento = $ (elemento);
    si (Prototype.Browser.IE) {
      var t = Element._attributeTranslations.read;
      if (t.values ​​[nombre]) return t.values ​​[nombre] (elemento, nombre);
      if (t.names [nombre]) name = t.names [nombre];
      if (name.include (':')) {
        volver (element.attributes | |! element.attributes [nombre])? null:
         element.attributes [nombre] valor.;
      }
    }
    volver Element.getAttribute (nombre);
  },

  WriteAttribute: function (elemento, nombre, valor) {
    elemento = $ (elemento);
    atributos var = {}, t = Element._attributeTranslations.write;

    if (typeof nombre == 'objeto') Atributos = nombre;
    más atributos [nombre] = Object.isUndefined (valor)? true: valor;

    para (attr var en atributos) {
      name = t.names [attr] | | attr;
      valor = atributos [attr];
      if (t.values ​​[attr]) name = t.values ​​[attr] (elemento, valor);
      if (valor === false | | === valor nulo)
        element.removeAttribute (nombre);
      else if (valor === true)
        element.setAttribute (nombre, nombre);
      element.setAttribute otro (nombre, valor);
    }
    volver elemento;
  },

  getHeight: function (elemento) {
    return $ (elemento) getDimensions () de altura..;
  },

  getWidth: function (elemento) {
    return $ (elemento) getDimensions () de ancho..;
  },

  classnames: función (elemento) {
    volver Element.ClassNames nuevas (elemento);
  },

  hasClassName: function (elemento, className) {
    si es una declaración (elemento (= $ (elemento))!);
    var = elementClassName element.className;
    retorno (elementClassName.length> 0 && (elementClassName className == | |
      nuevo RegExp ("(^ | \ \ s)" + + className "(\ \ s | $)") prueba (elementClassName))).;
  },

  addClassName: function (elemento, className) {
    si es una declaración (elemento (= $ (elemento))!);
    if (! element.hasClassName (className))
      element.className + = (? element.className '':'') + className;
    volver elemento;
  },

  removeClassName: function (elemento, className) {
    si es una declaración (elemento (= $ (elemento))!);
    element.className element.className.replace = (
      nuevo RegExp ("(^ | \ \ s +)" className + + "(\ \ s + | $)"), '') tira ();.
    volver elemento;
  },

  toggleClassName: function (elemento, className) {
    si es una declaración (elemento (= $ (elemento))!);
    volver elemento element.hasClassName [(className)?
      'RemoveClassName': 'addClassName'] (className);
  },

  / / Elimina los espacios en blanco sólo para los niños del nodo de texto
  cleanWhitespace: function (elemento) {
    elemento = $ (elemento);
    var node = element.firstChild;
    while (nodo) {
      var = nextNode node.nextSibling;
      if (node.nodeType == 3 &&! / \ S / .test (Node.nodeValue))
        element.removeChild (nodo);
      node = nextNode;
    }
    volver elemento;
  },

  función (elemento) {: vacío
    return $ (elemento) innerHTML.blank ().;
  },

  descendantOf: function (elemento, antepasado) {
    elemento = $ (elemento), antepasado = $ (antepasado);
    var = originalAncestor antepasado;

    if (element.compareDocumentPosition)
      return (element.compareDocumentPosition (antepasado) y 8) === 8;

    if (element.sourceIndex &&! Prototype.Browser.Opera) {
      var e = element.sourceIndex, un ancestor.sourceIndex =,
       nextAncestor = ancestor.nextSibling;
      if (! nextAncestor) {
        do {antepasado = ancestor.parentNode;}
        while ((nextAncestor = ancestor.nextSibling) ancestor.parentNode &&!);
      }
      if (&& nextAncestor nextAncestor.sourceIndex)
       volver (E> A && e <nextAncestor.sourceIndex);
    }

    while (= elemento element.parentNode)
      if (elemento == originalAncestor) return true;
    return false;
  },

  scrollTo: function (elemento) {
    elemento = $ (elemento);
    pos var = element.cumulativeOffset ();
    window.scrollTo (pos [0], pos [1]);
    volver elemento;
  },

  getStyle: function (elemento, estilo) {
    elemento = $ (elemento);
    style = estilo == 'float'? 'CssFloat': style.camelize ();
    var = valor element.style [estilo];
    if (! valor) {
      var css = document.defaultView.getComputedStyle (elemento, null);
      = valor css? css [estilo]: null;
    }
    if (estilo == 'opacidad') valor de retorno? parseFloat (valor): 1,0;
    devolver el valor == 'auto'? null: valor;
  },

  getOpacity: function (elemento) {
    return $ (elemento) getStyle ('opacidad').;
  },

  setStyle: function (elemento, estilos) {
    elemento = $ (elemento);
    var = elementStyle element.style, partido;
    if (Object.isString (estilos)) {
      element.style.cssText + = ';' + estilos;
      volver styles.include ('opacidad')?
        element.setOpacity (styles.match (/ opacidad: \ s * (\ d \ \ d *) /) [1]?.?): elemento;
    }
    para (var propiedad en estilos)
      if (propiedad == 'opacidad') element.setOpacity (estilos [propiedad]);
      más
        elementStyle [(propiedad == 'float' | | propiedad == 'cssFloat')?
          (Object.isUndefined (elementStyle.styleFloat) 'cssFloat': 'styleFloat'):
            propiedad] = estilos [propiedad];

    volver elemento;
  },

  setOpacity: function (elemento, valor) {
    elemento = $ (elemento);
    element.style.opacity = (value == 1 | | valor ==='')? '':
      (Valor <0,00001)? 0: valor;
    volver elemento;
  },

  getDimensions: función (elemento) {
    elemento = $ (elemento);
    var display = $ (elemento) getStyle ('display').;
    if (pantalla! = display 'none' &&! = null) / / error Safari
      retorno {width: element.offsetWidth, altura: element.offsetHeight};

    / / * Todas las propiedades Width y Height * 0 en dar elementos con ninguno de visualización,
    / / Para permitir que el elemento temporal
    var = els element.style;
    var = originalVisibility els.visibility;
    var = originalPosition els.position;
    var = originalDisplay els.display;
    els.visibility = 'oculta';
    els.position = "absoluta";
    els.display = 'block';
    var = originalWidth element.clientWidth;
    var = originalHeight element.clientHeight;
    els.display = originalDisplay;
    els.position = originalPosition;
    els.visibility = originalVisibility;
    retorno {width: originalWidth, altura: originalHeight};
  },

  makePositioned: function (elemento) {
    elemento = $ (elemento);
    var pos = Element.getStyle (elemento, 'position');
    si (pos == 'estático' | |! pos) {
      element._madePositioned = true;
      element.style.position = "relativo";
      / / Opera devuelve el desplazamiento con respecto al contexto de posicionamiento, cuando un
      / / Elemento es la posición relativa, pero la parte superior e izquierda no han sido definidos
      si (window.opera) {
        element.style.top = 0;
        element.style.left = 0;
      }
    }
    volver elemento;
  },

  undoPositioned: function (elemento) {
    elemento = $ (elemento);
    si (element._madePositioned) {
      element._madePositioned = undefined;
      element.style.position =
        element.style.top =
        element.style.left =
        element.style.bottom =
        element.style.right ='';
    }
    volver elemento;
  },

  makeClipping: function (elemento) {
    elemento = $ (elemento);
    if (element._overflow) elemento de retorno;
    element._overflow = Element.getStyle (element, 'overflow') | | 'auto';
    if (element._overflow! == 'oculto')
      element.style.overflow = 'oculta';
    volver elemento;
  },

  undoClipping: function (elemento) {
    elemento = $ (elemento);
    si el elemento de retorno (element._overflow!);
    element.style.overflow = element._overflow == 'auto'? '': Element._overflow;
    element._overflow = null;
    volver elemento;
  },

  cumulativeOffset: function (elemento) {
    var valueT = 0, valorl = 0;
    do {
      valueT + = element.offsetTop | | 0;
      valorl + = element.offsetLeft | | 0;
      = elemento element.offsetParent;
    } While (elemento);
    volver Element._returnOffset (valorl, valueT);
  },

  positionedOffset: function (elemento) {
    var valueT = 0, valorl = 0;
    do {
      valueT + = element.offsetTop | | 0;
      valorl + = element.offsetLeft | | 0;
      = elemento element.offsetParent;
      if (elemento) {
        if (Element.tagName == 'Cuerpo') break;
        var p = Element.getStyle (elemento, 'position');
        if (p == 'estático'!) break;
      }
    } While (elemento);
    volver Element._returnOffset (valorl, valueT);
  },

  absolutizar: function (elemento) {
    elemento = $ (elemento);
    if (element.getStyle ('position') == 'absoluta') return;
    / / Position.prepare (); / / Para hacer manualmente por Scripty cuando lo necesita.

    compensaciones var = element.positionedOffset ();
    var top = compensaciones [1];
    var left = compensaciones [0];
    var width = element.clientWidth;
    var height = element.clientHeight;

    element._originalLeft = left - parseFloat (element.style.left | | 0);
    element._originalTop = top - parseFloat (element.style.top | | 0);
    element._originalWidth = element.style.width;
    element._originalHeight = element.style.height;

    element.style.position = "absoluta";
    element.style.top = top + "px";
    element.style.left = left + 'px';
    element.style.width = ancho + "px";
    element.style.height = altura + "px";
    volver elemento;
  },

  relativizan: function (elemento) {
    elemento = $ (elemento);
    if (element.getStyle ('position') == 'relativa') return;
    / / Position.prepare (); / / Para hacer manualmente por Scripty cuando lo necesita.

    element.style.position = "relativo";
    var top = parseFloat (element.style.top | | 0) - (element._originalTop | | 0);
    var left = parseFloat (element.style.left | | 0) - (element._originalLeft | | 0);

    element.style.top = top + "px";
    element.style.left = left + 'px';
    element.style.height = element._originalHeight;
    element.style.width = element._originalWidth;
    volver elemento;
  },

  cumulativeScrollOffset: function (elemento) {
    var valueT = 0, valorl = 0;
    do {
      valueT + = element.scrollTop | | 0;
      valorl + = element.scrollLeft | | 0;
      = elemento element.parentNode;
    } While (elemento);
    volver Element._returnOffset (valorl, valueT);
  },

  getOffsetParent: function (elemento) {
    if (element.offsetParent) return $ (element.offsetParent);
    if (elemento == document.body) return $ (elemento);

    while ((= elemento element.parentNode) elemento &&! = document.body)
      if (Element.getStyle (element, 'position')! = 'estático')
        return $ (elemento);

    return $ (document.body);
  },

  viewportOffset: function (forElement) {
    var valueT = 0, valorl = 0;

    var elemento = forElement;
    do {
      valueT + = element.offsetTop | | 0;
      valorl + = element.offsetLeft | | 0;

      / / Safari fix
      si (== element.offsetParent document.body &&
        Element.getStyle (element, 'position') == 'absoluta') break;

    } While (= elemento element.offsetParent);

    = elemento forElement;
    do {
      if (Prototype.Browser.Opera | | Element.tagName == 'Cuerpo') {
        valueT - element.scrollTop = | | 0;
        valorl - element.scrollLeft = | | 0;
      }
    } While (= elemento element.parentNode);

    volver Element._returnOffset (valorl, valueT);
  },

  clonePosition: function (elemento, fuente) {
    var options = Object.extend ({
      SetLeft: true,
      SetTop: true,
      setWidth: true,
      setHeight: true,
      offsetTop: 0,
      offsetLeft: 0
    }, Los argumentos [2] | | {});

    / / Buscar posición de la página de origen
    fuente = $ (fuente);
    var p = source.viewportOffset ();

    / / Encuentra el sistema de coordenadas de usar
    elemento = $ (elemento);
    var delta = [0, 0];
    var parent = null;
    / / Delta [0,0] hará muy bien con position: elementos fijos,
    / / Posición: Las necesidades absolutas deltas offsetParent
    if (Element.getStyle (element, 'position') == 'absoluta') {
      parent = element.getOffsetParent ();
      delta = parent.viewportOffset ();
    }

    / / Corregir por las compensaciones del cuerpo (corrección de Safari)
    if (padre == document.body) {
      delta [0] - = document.body.offsetLeft;
      delta [1] - = document.body.offsetTop;
    }

    / / Establecer la posición
    if (options.setLeft) element.style.left = (p [0] - delta [0] + options.offsetLeft) + "px";
    if (options.setTop) element.style.top = (p [1] - delta [1] + options.offsetTop) + "px";
    if (options.setWidth) element.style.width = source.offsetWidth + "px";
    if (options.setHeight) element.style.height = source.offsetHeight + "px";
    volver elemento;
  }
};

Element.Methods.identify.counter = 1;

Object.extend (Element.Methods, {
  getElementsBySelector: Element.Methods.select,
  childElements: Element.Methods.immediateDescendants
});

Element._attributeTranslations = {
  escribir: {
    nombres: {
      className: «clase»,
      htmlFor: 'para'
    },
    valores: {}
  }
};

si (Prototype.Browser.Opera) {
  Element.Methods.getStyle Element.Methods.getStyle.wrap = (
    función (vaya, elemento, estilo) {
      switch (estilo) {
        caso de 'izquierda': 'top' caso: Caso de 'derecho': 'bottom' caso:
          if (elemento proceder (, 'position') === 'estático') return null;
        case 'height': case 'width':
          / / Devuelve '0 px 'para los elementos ocultos, queremos que devuelva null
          si return null (Element.visible (elemento)!);

          / / Devuelve las dimensiones de la caja de fronteras más que el contenido de la caja
          / / Dimensiones, por lo que reste de relleno y los bordes del valor
          var dim = parseInt (PROCEED (elemento, estilo), 10);

          if (dim! == elemento ['compensar' + style.capitalize ()])
            devolver dim + "px";

          var las propiedades;
          if (estilo === 'height') {
            properties = ['border-top-width', 'padding-top',
             'Padding-bottom', 'border-bottom-width'];
          }
          else {
            properties = ['border-left-width', 'padding-left',
             'Padding-right', 'border-right-width'];
          }
          volver properties.inject (función dim, (memo, propiedad) {
            var val = proceder (elemento, la propiedad);
            volver val === nulo? nota: memo - parseInt (val, 10);
          }) + "Px";
        default: return proceder (elemento, estilo);
      }
    }
  );

  Element.Methods.readAttribute Element.Methods.readAttribute.wrap = (
    función (vaya, elemento, atributo) {
      if (atributo === 'title') element.title retorno;
      volver proceder (elemento, atributo);
    }
  );
}

else if (Prototype.Browser.IE) {
  / / IE no informa correctamente las compensaciones para los elementos estáticos, por lo que los cambios
  / / Para "relativa" para obtener los valores, a continuación, cambiar de nuevo.
  Element.Methods.getOffsetParent Element.Methods.getOffsetParent.wrap = (
    función (vaya, elemento) {
      elemento = $ (elemento);
      var = posición element.getStyle ('position');
      if (! == posición "estática") devuelven proceder (elemento);
      element.setStyle ({position: 'pariente'});
      var = valor proceder (elemento);
      element.setStyle ({position: posición});
      valor de retorno;
    }
  );

  $ W ('positionedOffset viewportOffset'). Cada una (función (método) {
    Element.Methods [método] = Element.Methods [método]. Wrap (
      función (vaya, elemento) {
        elemento = $ (elemento);
        var = posición element.getStyle ('position');
        if (! == posición "estática") devuelven proceder (elemento);
        / / Trigger hasLayout en el padre compensar modo que los informes IE6
        / / OffsetTop precisa y valores offsetLeft para la posición: fijo.
        var offsetParent element.getOffsetParent = ();
        if (offsetParent.getStyle offsetParent && ('position') === 'fijo')
          offsetParent.setStyle ({zoom: 1});
        element.setStyle ({position: 'pariente'});
        var = valor proceder (elemento);
        element.setStyle ({position: posición});
        valor de retorno;
      }
    );
  });

  Element.Methods.getStyle = function (elemento, estilo) {
    elemento = $ (elemento);
    style = (estilo == 'float' | | estilo == 'cssFloat')? 'StyleFloat': style.camelize ();
    var = valor element.style [estilo];
    si el valor = element.currentStyle [estilo] (&& valor element.currentStyle!);

    if (estilo == 'opacidad') {
      if (valor = (element.getStyle ('filtro') | |'') coincide (/ alpha \ (opacidad = (*) \) /).).
        if (valor [1]) parseFloat retorno (valor [1]) / 100;
      devolver 1,0;
    }

    if (value == 'auto') {
      if (estilo (== 'width' | | El estilo == 'height') && (element.getStyle ('display') = 'none'))
        volver elemento ['compensar' + style.capitalize ()] + "px";
      return null;
    }
    valor de retorno;
  };

  Element.Methods.setOpacity = function (elemento, valor) {
    función stripAlpha (filtro) {
      volver filter.replace (/ alpha \ ([^ \)] * \) / gi,'');
    }
    elemento = $ (elemento);
    var = currentStyle element.currentStyle;
    if ((&& currentStyle currentStyle.hasLayout)! | |
      (! && CurrentStyle element.style.zoom == 'normal'))
        element.style.zoom = 1;

    var filter = element.getStyle ('filter'), style = elemento. estilo;
    if (value == 1 | | valor ==='') {
      (Filter = stripAlpha (filtro))?
        style.filter = filtro: style.removeAttribute ('filtro');
      volver elemento;
    } Else if (valor <0,00001) valor = 0;
    style.filter = stripAlpha (filtro) +
      'Alfa (opacidad =' + (valor * 100) + ')';
    volver elemento;
  };

  Element._attributeTranslations = {
    leer: {
      nombres: {
        'Class': "className",
        'Por': 'htmlFor'
      },
      valores: {
        _getAttr: function (elemento, atributo) {
          volver Element.getAttribute (atributo, 2);
        },
        _getAttrNode: function (elemento, atributo) {
          var node = element.getAttributeNode (atributo);
          volver nodo? node.value: "";
        },
        _getEv: function (elemento, atributo) {
          atribuir Element.getAttribute = (atributo);
          volver atributo? . attribute.toString () slice (23, -2): null;
        },
        _flag: function (elemento, atributo) {
          return $ (elemento). hasAttribute (atributo)? atributo: null;
        },
        estilo: function (elemento) {
          volver element.style.cssText.toLowerCase ();
        },
        Título: function (elemento) {
          volver element.title;
        }
      }
    }
  };

  Element._attributeTranslations.write = {
    nombres: Object.extend ({
      cellpadding: "cellpadding ',
      cellspacing: 'cellSpacing'
    }, Element._attributeTranslations.read.names),
    valores: {
      comprobado: function (elemento, valor) {
        = element.checked valor!!;
      },

      estilo: function (elemento, valor) {
        element.style.cssText = valor? valor:'';
      }
    }
  };

  Element._attributeTranslations.has = {};

  $ W ("colspan rowSpan valign dateTime accessKey tabIndex '+
      'Enctype maxLength readOnly longdesc "). Cada uno (function (attr) {
    Element._attributeTranslations.write.names [attr.toLowerCase ()] = attr;
    Element._attributeTranslations.has [attr.toLowerCase ()] = attr;
  });

  (Función (v) {
    Object.extend (v, {
      href: v._getAttr,
      src: v._getAttr,
      Tipo: v._getAttr,
      acción: v._getAttrNode,
      disabled: v._flag,
      verificado: v._flag,
      readonly: v._flag,
      múltiple: v._flag,
      onload: v._getEv,
      onunload: v._getEv,
      onclick: v._getEv,
      ondblclick: v._getEv,
      onmousedown: v._getEv,
      onmouseup: v._getEv,
      onmouseover: v._getEv,
      onmousemove: v._getEv,
      onmouseout: v._getEv,
      onfocus: v._getEv,
      onblur: v._getEv,
      onkeypress: v._getEv,
      onkeydown: v._getEv,
      onkeyup: v._getEv,
      onsubmit: v._getEv,
      onreset: v._getEv,
      onselect: v._getEv,
      onchange: v._getEv
    });
  }) (Element._attributeTranslations.read.values);
}

else if (Prototype.Browser.Gecko && / rv: 1 \ 0.8 \ .0/.test (navigator.userAgent)) {
  Element.Methods.setOpacity = function (elemento, valor) {
    elemento = $ (elemento);
    element.style.opacity = (valor == 1)? 0.999999:
      (Valor ==='')? '': (Valor <0,00001)? 0: valor;
    volver elemento;
  };
}

else if (Prototype.Browser.WebKit) {
  Element.Methods.setOpacity = function (elemento, valor) {
    elemento = $ (elemento);
    element.style.opacity = (value == 1 | | valor ==='')? '':
      (Valor <0,00001)? 0: valor;

    if (value == 1)
      if (Element.tagName element.width == 'IMG' &&) {
        element.width + +; element.width--;
      Try {} else
        var n = document.createTextNode ('');
        element.appendChild (n);
        element.removeChild (n);
      } Catch (e) {}

    volver elemento;
  };

  / / Devuelve Safari márgenes en el cuerpo que no es correcto si el niño es absolutamente
  / / Posición. Por motivos de rendimiento, redefinir Elemento # cumulativeOffset para
  / / KHTML / Webkit solamente.
  Element.Methods.cumulativeOffset = function (elemento) {
    var valueT = 0, valorl = 0;
    do {
      valueT + = element.offsetTop | | 0;
      valorl + = element.offsetLeft | | 0;
      si (== element.offsetParent document.body)
        if (Element.getStyle (element, 'position') == 'absoluta') break;

      = elemento element.offsetParent;
    } While (elemento);

    volver Element._returnOffset (valorl, valueT);
  };
}

si (Prototype.Browser.IE | | Prototype.Browser.Opera) {
  / / IE y Opera están desaparecidos. InnerHTML apoyo para los elementos relacionados con tablas y SELECT
  Element.Methods.update = function (elemento, contenido) {
    elemento = $ (elemento);

    if (contenido content.toElement &&) contenido content.toElement = ();
    if (Object.isElement (contenido)) return element.update () insert (contenido).;

    content = Object.toHTML (contenido);
    var tagName element.tagName.toUpperCase = ();

    if (tagName en Element._insertionTranslations.tags) {
      $ A (element.childNodes) cada uno (function (nodo) {element.removeChild (nodo)}).;
      Element._getContentFromAnonymousElement (tagName, content.stripScripts ())
        . Cada uno (función (nodo) {element.appendChild (nodo)});
    }
    más element.innerHTML = content.stripScripts ();

    . content.evalScripts.bind (contenido) aplazar ();
    volver elemento;
  };
}

if ('outerHTML' en document.createElement ('div')) {
  Element.Methods.replace = function (elemento, contenido) {
    elemento = $ (elemento);

    if (contenido content.toElement &&) contenido content.toElement = ();
    si (Object.isElement (contenido)) {
      element.parentNode.replaceChild (contenido, elemento);
      volver elemento;
    }

    content = Object.toHTML (contenido);
    var parent = element.parentNode, tagName parent.tagName.toUpperCase = ();

    if (Element._insertionTranslations.tags [tagName]) {
      var nextSibling element.next = ();
      fragmentos var = Element._getContentFromAnonymousElement (tagName, content.stripScripts ());
      parent.removeChild (elemento);
      if (nextSibling)
        fragments.each (función (nodo) {parent.insertBefore (nodo, nextSibling)});
      más
        fragments.each (función (nodo) {parent.appendChild (nodo)});
    }
    más element.outerHTML = content.stripScripts ();

    . content.evalScripts.bind (contenido) aplazar ();
    volver elemento;
  };
}

Element._returnOffset = función (l, t) {
  var resultado = [l, l];
  result.left = l;
  result.top = t;
  return resultado;
};

Element._getContentFromAnonymousElement = function (tagName, html) {
  var div = new Element ('div'), t = Element._insertionTranslations.tags [tagName];
  si (t) {
    div.innerHTML = t [0] + html + t [1];
    t [2] veces (function () {div = div.firstChild}).;
  } Else div.innerHTML = html;
  return $ A (div.childNodes);
};

Element._insertionTranslations = {
  antes: function (elemento del nodo) {
    element.parentNode.insertBefore (nodo, elemento);
  },
  arriba: function (elemento del nodo) {
    element.insertBefore (nodo, element.firstChild);
  },
  fondo: function (elemento del nodo) {
    element.appendChild (nodo);
  },
  después: function (elemento del nodo) {
    element.parentNode.insertBefore (nodo, element.nextSibling);
  },
  tags: {
    TABLA: ['<table>', '</ table> ", 1],
    TBODY: ['<table> <tbody>', '</ tbody> </ table>', 2],
    TR: ['<table> <tbody> <tr>', '</ tr> </ tbody> </ table>', 3],
    TD: ['<table> <tbody> <tr> <td>', '</ td> </ tr> </ tbody> </ table>', 4],
    SELECT: ['SELECT', '</ select> ", 1]
  }
};

(Function () {
  Object.extend (this.tags, {
    THEAD: this.tags.TBODY,
    TFOOT: this.tags.TBODY,
    TH: this.tags.TD
  });
.}) Llame al (Element._insertionTranslations);

Element.Methods.Simulated = {
  hasAttribute: function (elemento, atributo) {
    atributo = Element._attributeTranslations.has [atributo] | | atributo;
    var nodo = $ (elemento) getAttributeNode (atributo).;
    volver && nodo node.specified;
  }
};

Element.Methods.ByTag = {};

Object.extend (Elemento, Element.Methods);

if (! Prototype.BrowserFeatures.ElementExtensions &&
    document.createElement ('div'). __proto__) {
  window.HTMLElement = {};
  window.HTMLElement.prototype = document.createElement ('div') __proto__.;
  Prototype.BrowserFeatures.ElementExtensions = true;
}

Element.extend = (function () {
  if (Prototype.BrowserFeatures.SpecificElementExtensions)
    volver Prototype.K;

  Métodos var = {}, ByTag = Element.Methods.ByTag;

  var = extend Object.extend (function (elemento) {
    if (elemento | | element._extendedByPrototype | |
        element.nodeType = 1 | | elemento == ventana) elemento de retorno!;

    métodos var = Object.clone (Métodos),
      tagName = Element.tagName, propiedad, valor;

    / / Extender los métodos de etiquetas específicas
    if (ByTag [tagName]) Object.extend (métodos, ByTag [tagName]);

    para (propiedad en métodos) {
      = Valor métodos [propiedad];
      if (Object.isFunction (valor) &&! (propiedad de elemento))
        elemento [propiedad] = value.methodize ();
    }

    element._extendedByPrototype = Prototype.emptyFunction;
    volver elemento;

  }, {
    actualización: function () {
      / / Métodos para extender todas las etiquetas (Safari no necesita esto)
      if (! Prototype.BrowserFeatures.ElementExtensions) {
        Object.extend (Métodos, Element.Methods);
        Object.extend (Métodos, Element.Methods.Simulated);
      }
    }
  });

  extend.refresh ();
  volver extender;
}) ();

Element.hasAttribute = function (elemento, atributo) {
  if (element.hasAttribute) element.hasAttribute retorno (atributo);
  volver Element.Methods.Simulated.hasAttribute (elemento, atributo);
};

Element.addMethods = function (métodos) {
  var f = Prototype.BrowserFeatures, T = Element.Methods.ByTag;

  if (! métodos) {
    Object.extend (Forma, Form.Methods);
    Object.extend (Form.Element, Form.Element.Methods);
    Object.extend (Element.Methods.ByTag, {
      "FORMA": Object.clone (Form.Methods),
      "INPUT": Object.clone (Form.Element.Methods),
      "SELECT": Object.clone (Form.Element.Methods),
      "TEXTAREA": Object.clone (Form.Element.Methods)
    });
  }

  if (arguments.length == 2) {
    var = tagName métodos;
    métodos argumentos = [1];
  }

  si Object.extend (Element.Methods, métodos | | {}) (tagName!);
  else {
    if (Object.isArray (tagName)) tagName.each (Extend);
    más extend (tagName);
  }

  función extend (tagName) {
    tagName tagName.toUpperCase = ();
    if (! Element.Methods.ByTag [tagName])
      Element.Methods.ByTag [tagName] = {};
    Object.extend (Element.Methods.ByTag tagName [], métodos);
  }

  función de copia (métodos, destino, onlyIfAbsent) {
    onlyIfAbsent = onlyIfAbsent | | false;
    para (var propiedad en métodos) {
      var = value métodos [propiedad];
      (! Object.isFunction (valor)) si continuar;
      if (onlyIfAbsent | |! (propiedad en destino))
        destino [propiedad] = value.methodize ();
    }
  }

  función findDOMClass (tagName) {
    var klass;
    var trans = {
      "OPTGROUP": "OPTGROUP", "TEXTAREA": "TextArea", "P": "Párrafo",
      "FIELDSET": "fieldset", "UL": "ulist", "OL": "olist", "DL": "DLIST"
      "DIR": "Directorio", "H1": "Título", "H2": "Título", "H3": "Título",
      "H4": "Título", "H5": "Título", "H6": "Título", "Q": "Cita",
      "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Imagen", "TITULO":
      "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
      "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
      "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
      "Frameset", "IFRAME": "iframe"
    };
    if (trans [tagName]) klass = 'HTML' + trans [tagName] + 'Element';
    if (ventana [klass]) Ventana de retorno [klass];
    klass = 'HTML' + tagName + 'Element';
    if (ventana [klass]) Ventana de retorno [klass];
    klass = 'HTML' + tagName.capitalize () + 'Elemento';
    if (ventana [klass]) Ventana de retorno [klass];

    ventana [klass] = {};
    ventana [klass] prototipo = document.createElement (tagName) __proto__..;
    regresar ventana [klass];
  }

  si (F.ElementExtensions) {
    copia (Element.Methods, HTMLElement.prototype);
    copia (Element.Methods.Simulated, HTMLElement.prototype, true);
  }

  si (F.SpecificElementExtensions) {
    para (etiqueta var en Element.Methods.ByTag) {
      klass var = findDOMClass (tag);
      if (Object.isUndefined (klass)) continuar;
      copia (T [tag], klass.prototype);
    }
  }

  Object.extend (Elemento, Element.Methods);
  eliminar Element.ByTag;

  if (Element.extend.refresh) Element.extend.refresh ();
  Element.cache = {};
};

document.viewport = {
  getDimensions: function () {
    dimensiones var = {};
    var B = Prototype.Browser;
    $ W ('height width'). Cada uno (function (d) {
      var D = d.capitalize ();
      dimensiones [d] = (&& B.WebKit! document.evaluate)? auto ['interior' + D]:
        (B.Opera)? document.body ['cliente' + D]: document.documentElement ['cliente' + D];
    });
    volver dimensiones;
  },

  getWidth: function () {
    this.getDimensions Volver () de ancho.;
  },

  getHeight: function () {
    this.getDimensions Volver () de altura.;
  },

  getScrollOffsets: function () {
    volver Element._returnOffset (
      window.pageXOffset | | document.documentElement.scrollLeft | | document.body.scrollLeft,
      window.pageYOffset | | document.documentElement.scrollTop | | document.body.scrollTop);
  }
};
/ * Porciones de la clase Selector se derivan de Jack Slocumâ DomQuery € ™ s,
 * Parte de YUI-Ext versión 0.40, distribuido bajo los términos de un tipo MIT
 * Licencia. Por favor, consulte http://www.yui-ext.com/ para más información. * /

var = Selector Class.create ({
  initialize: function (expresión) {
    this.expression expression.strip = ();
    this.compileMatcher ();
  },

  shouldUseXPath: function () {
    si es una declaración falsa (Prototype.BrowserFeatures.XPath!);

    var e = this.expression;

    / / Safari 3 atraganta con: *-de-tipo y vacías:
    if (&& Prototype.Browser.WebKit
     (E.include ("-of-type") | | e.include (": vacío")))
      return false;

    / / XPath no puede hacer atributos de espacios de nombres, ni puede leer
    / / La "marcada" propiedad de nodos DOM
    if ((/ (\ [[\ w-] *: |:. revisado) /) prueba (this.expression))
      return false;

    return true;
  },

  compileMatcher: function () {
    si (this.shouldUseXPath ())
      volver this.compileXPathMatcher ();

    var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
        c = Selector.criteria, le, p, m;

    if (Selector._cache [e]) {
      this.matcher Selector._cache = [e];
      volver;
    }

    this.matcher = ["this.matcher = function (root) {",
                    "Var r = root, h = Selector.handlers, c = false, n;"];

    mientras que (e && le! = e && (/ \ / S). prueba (e)) {
      le = e;
      for (var i en ps) {
        p = ps [i];
        si (m = e.match (p)) {
          this.matcher.push (Object.isFunction (c [i]) c [i] (m)?:
    	      nueva plantilla (c [i]) evaluar (m)).;
          e = e.replace (m [0],'');
          break;
        }
      }
    }

    this.matcher.push ("retorno h.unique (n); \ n}");
    eval (this.matcher.join ('\ n'));
    Selector._cache [this.expression] = this.matcher;
  },

  compileXPathMatcher: function () {
    var e = this.expression, PS = Selector.patterns,
        x = Selector.xpath, le, m;

    if (Selector._cache [e]) {
      this.xpath Selector._cache = [e]; retorno;
    }

    this.matcher = ['/ / *.'];
    mientras que (e && le! = e && (/ \ / S). prueba (e)) {
      le = e;
      for (var i en ps) {
        if (m = e.match (p [i])) {
          this.matcher.push (Object.isFunction (x [i]) x [i] (m)?:
            nueva plantilla (x [i]) evaluar (m)).;
          e = e.replace (m [0],'');
          break;
        }
      }
    }

    this.xpath = this.matcher.join ('');
    Selector._cache [this.expression] = this.xpath;
  },

  findElements: function (root) {
    root = root | | documento;
    if (this.xpath) return document._getElementsByXPath (this.xpath, root);
    volver this.matcher (root);
  },

  partido: function (elemento) {
    this.tokens = [];

    var e = this.expression, PS = Selector.patterns, como = Selector.assertions;
    var LE, p, m;

    mientras que (e && Le! == e && (/ \ / S). prueba (e)) {
      le = e;
      for (var i en ps) {
        p = ps [i];
        si (m = e.match (p)) {
          / / Utilizar los métodos Selector.assertions a menos que el selector
          / / Es demasiado complejo.
          si (como [i]) {
            this.tokens.push ([i, Object.clone (m)]);
            e = e.replace (m [0],'');
          } Else {
            / / Regañadientes hacer una búsqueda a nivel de documento
            / / Y buscar una coincidencia en la matriz
            . this.findElements retorno (documento) incluyen (elemento);
          }
        }
      }
    }

    var partido = true, el nombre, los partidos;
    for (var i = 0, token, token = this.tokens [i], i + +) {
      name = token [0] = token, fósforos [1];
      if (! Selector.assertions [nombre] (elemento, partidos)) {
        match = false; break;
      }
    }

    volver coinciden;
  },

  toString: function () {
    volver this.expression;
  },

  inspeccionar: function () {
    regresar "# <Selector:" + + this.expression.inspect() ">";
  }
});

Object.extend (Selector, {
  _cache: {}

  xpath: {
    descendiente: "/ / *",
    hijo: "/ *",
    adyacente: "/ following-sibling :: * [1]",
    laterSibling: '/ following-sibling :: *',
    tagName: function (m) {
      if (m [1] == '*') return'';
      return "[local-name () = '" + m [1]. toLowerCase () +
             "'O local-() ='" + m [1] toUpperCase () + "']".;
    },
    className: "[contains (concat ('', @ class, ''), '# {1}')]",
    id: "[@ id = '# {1}']",
    attrPresence: function (m) {
      m [1] = M [1] toLowerCase ().;
      volver plantilla nueva ("[@ # {1}]") evaluar (m).;
    },
    attr: function (m) {
      m [1] = M [1] toLowerCase ().;
      m [3] = m [5] | | m [6];
      volver plantilla nueva (Selector.xpath.operators [m [2]]) evaluar (m).;
    },
    seudo: function (m) {
      var h = Selector.xpath.pseudos [m [1]];
      si el retorno'' (h!);
      si (Object.isFunction (h)) return h (m);
      volver plantilla nueva (Selector.xpath.pseudos [m [1]]) evaluar (m).;
    },
    operadores: {
      '=': "[@ # {1} = '# {3}']",
      '! =': "[! @ # {1} = '# {3}']",
      '^ =': "[Starts-with (@ # {1}, '# {3}')]",
      '$ =': "[Substring (@ # {1}, (string-length (@ # {1}) - string-length ('# {3}') + 1)) = '# {3}'] ",
      '* =': "[Contains (@ # {1}, '# {3}')]",
      '~ =': "[Contains (concat ('', @ # {1}, ''), '# {3}')]",
      '| =': "[Contains (concat ('-', @ # {1}, '-'), '- # {3} -')]"
    },
    pseudos: {
      'First-child': '[no (preceding-sibling :: *)]',
      'Last-child': '[no (following-sibling :: *)]',
      «Hijo único»: «[no (preceding-sibling :: * o siguiente hermano-:: *)] ',
      "Vacía": "[cuenta (*) = 0 y (contar texto (()) = 0 o traducir (texto (), '\ t \ r \ n','') ='')]",
      "Marcado": "[@ checked]",
      'Desactivado': "[@ disabled]",
      'Habilitado': "[not (@ disabled)]",
      'No': function (m) {
        var e = m [6], p = Selector.patterns,
            x = Selector.xpath, le, v;

        var exclusión = [];
        mientras que (e && le! = e && (/ \ / S). prueba (e)) {
          le = e;
          for (var i en p) {
            if (m = e.match (p [i])) {
              v = Object.isFunction (x [i])? x [i] (m):. nueva plantilla (x [i]) evaluar (m);
              exclusion.push ("(" v.substring + (1, v.length - 1) + ")");
              e = e.replace (m [0],'');
              break;
            }
          }
        }
        return "[not (" + exclusion.join ("y") + ")]";
      },
      'Nth-child': function (m) {
        volver Selector.xpath.pseudos.nth ("(count (/ preceding-sibling :: *) + 1).", m);
      },
      'N-ésimo last-child': function (m) {
        volver Selector.xpath.pseudos.nth ("(count (/ following-sibling :: *) + 1).", m);
      },
      'Nth-of-type': function (m) {
        volver Selector.xpath.pseudos.nth ("position ()", m);
      },
      'N-ésimo pasado-of-type': function (m) {
        volver Selector.xpath.pseudos.nth ("(último () + 1 - Posición ())", m);
      },
      "El primero de tipo ': function (m) {
        m [6] = "1"; Selector.xpath.pseudos retorno ['nth-of-type'] (m);
      },
      'Last-of-type': function (m) {
        m [6] = "1"; Selector.xpath.pseudos retorno ['n-ésimo pasado-of-type'] (m);
      },
      'Sólo-de-type': function (m) {
        var p = Selector.xpath.pseudos, retorno p ['first-of-type'] (m) + p ['last-of-type'] (m);
      },
      nth: function (fragmento, m) {
        var mm, la fórmula = m [6], predicado;
        if (fórmula == 'incluso'), la fórmula = '2 +0' n;
        si (== fórmula "impar"), la fórmula = '2 +1' n;
        if (mm = formula.match (/ ^ (\ d +) $ /)) / / solo dígito
          return '[' + fragmento + "=" + mm [1] + ']';
        if (mm = formula.match (/ ^ (- \ d *) n ((? [+ -]?) (\ d +)) /)) {/ / un b +
          si (mm [1] == "-") mm [1] = -1;
          var a = mm [1]? Número (mm [1]): 1;
          var b = mm [2]? Número (mm [2]): 0;
          predicado = "[((# {fragmento} - # {b}) mod # {a} = 0) y" +
          "((# {Fragmento} - # {b}) div # {a}> = 0)]";
          volver plantilla nueva (predicado). evaluar ({
            fragmento: fragmento, a: a, b: b});
        }
      }
    }
  },

  criterios: {
    tagName: 'n = h.tagName (n, r, "# {1}", c); c = false;'
    className: 'n h.className = (n, r, "# {1}", c); c = false;'
    id: 'n = h.id (n, r, "# {1}", c); c = false; ",
    attrPresence: 'n = h.attrPresence (n, r, "# {1}", c); c = false;'
    attr: function (m) {
      m [3] = (m [5] | | m [6]);
      volver plantilla nueva. ('n = h.attr (n, r, "# {1}", "# {3}", "# {2}", c); c = false; ") evaluar (m) ;
    },
    seudo: function (m) {
      if (m) [6] m [6] = m [6] replace (/ "/ g, '\ \".')
      volver plantilla nueva ('n = h.pseudo (n, "# {1}", "# {6}", r, c) c = false; "). evaluar (m);
    },
    descendiente: "c =" descendiente "; '
    hijo: "c =" niño "; ',
    adyacente: "c =" adyacente "; ',
    laterSibling: "c =" laterSibling "; '
  },

  patrones: {
    / / Combinadores debe aparecer en primer lugar
    / / (Y descendiente tiene que ser el último combinador)
    laterSibling: / ^ \ s * ~ \ s * /,
    niño: / ^ \ s *> \ s * /,
    adyacente: / ^ \ s * \ + \ s * /,
    descendiente: / ^ \ s /,

    / / Selectores seguir
    tagName: / ^ \ s * (\ * | [\ w \ -] +) (\ b | $) /,
    Identificación: / ^ # ([\ w \ - \ *] +) (\ b | $) /,
    className:. / ^ \ ([\ w \ - \ *] +) (\ b | $) /,
    seudo:

    attrPresence: / ^ \ [([\ w] +) \] /,
    attr: / \ [((: [\ w-] * :) [\ w-] +) \ s * (:???? ([^ $ * ~ |] =) \ s * (([' "]) ([^ \ 4] *) \ 4 |? ([^" ¿?] [^ \]] *))) \] /
  },

  / / Para Selector.match y Element # coincidir
  afirmaciones: {
    tagName: function (elemento, partidos) {
      partidos de vuelta [1] toUpperCase () == element.tagName.toUpperCase ().;
    },

    className: function (elemento, partidos) {
      volver Element.hasClassName (elemento, coincidencias [1]);
    },

    Identificación: function (elemento, partidos) {
      volver element.id === coincidencias [1];
    },

    attrPresence: function (elemento, partidos) {
      volver Element.hasAttribute (elemento, coincidencias [1]);
    },

    attr: function (elemento, partidos) {
      var = nodeValue Element.readAttribute (elemento, coincidencias [1]);
      volver nodeValue && Selector.operators [partidos [2]] (nodeValue, partidos [5] | | partidos [6]);
    }
  },

  manipuladores: {
    / / Funciones de utilidad
    / / Une dos colecciones
    concat: función (a, b) {
      for (var i = 0, el nodo, nodo = b [i]; i + +)
        a.push (nodo);
      volver a;
    },

    / / Marca una matriz de nodos para el recuento
    marca: function (nodos) {
      var = _true Prototype.emptyFunction;
      for (var i = 0, el nodo, nodo nodos = [i], i + +)
        node._countedByPrototype = _true;
      los nodos de retorno;
    },

    desmarcar: function (nodos) {
      for (var i = 0, el nodo, nodo nodos = [i], i + +)
        node._countedByPrototype = undefined;
      los nodos de retorno;
    },

    / / Marcar cada nodo hijo con su posición (para llamadas NTH)
    / / "OfType" bandera indica si estamos indexando por enésima-de-tipo
    / / En lugar de nth-child
    índice: function (parentNode, marcha atrás, OfType) {
      parentNode._countedByPrototype = Prototype.emptyFunction;
      if (inverso) {
        para (nodos var = parentNode.childNodes, i = nodes.length - 1, j = 1; i> = 0; i -) {
          var nodo = ganglios [i];
          if (node.nodeType == 1 && (OfType | | node._countedByPrototype)!) node.nodeIndex = j + +;
        }
      } Else {
        for (var i = 0, j = 1, nodos = parentNode.childNodes; nodo nodos = [i], i + +)
          if (node.nodeType == 1 && (OfType | | node._countedByPrototype)!) node.nodeIndex = j + +;
      }
    },

    / / Filtra duplicados y se extiende a todos los nodos
    único: function (nodos) {
      if (nodes.length == 0) nodos de retorno;
      resultados var = [], n;
      for (var i = 0, l = nodes.length; i <l; i + +)
        if (! (n = ganglios [i]). _countedByPrototype) {
          n._countedByPrototype = Prototype.emptyFunction;
          results.push (Element.extend (n));
        }
      volver Selector.handlers.unmark (resultados);
    },

    / / FUNCIONES Combinator
    descendiente: function (nodos) {
      var h = Selector.handlers;
      for (var i = 0, resultado = [], nodo, nodo nodos = [i], i + +)
        h.concat (resultados, node.getElementsByTagName ('*'));
      devuelven resultados;
    },

    niño: function (nodos) {
      var h = Selector.handlers;
      for (var i = 0, resultado = [], nodo, nodo = ganglios [i], i + +) {
        for (var j = 0, niño, niño = node.childNodes [j]; j + +)
          if (! '!' child.nodeType child.tagName == 1 && =) results.push (niño);
      }
      devuelven resultados;
    },

    función (nodos) {: adyacente
      for (var i = 0, resultado = [], nodo, nodo = ganglios [i], i + +) {
        var siguiente = this.nextElementSibling (nodo);
        if (siguiente) results.push (siguiente);
      }
      devuelven resultados;
    },

    laterSibling: function (nodos) {
      var h = Selector.handlers;
      for (var i = 0, resultado = [], nodo, nodo nodos = [i], i + +)
        h.concat (resultados, Element.nextSiblings (nodo));
      devuelven resultados;
    },

    nextElementSibling: function (nodo) {
      while (nodo node.nextSibling =)
	      if (node.nodeType == 1) nodo de retorno;
      return null;
    },

    previousElementSibling: function (nodo) {
      while (nodo node.previousSibling =)
        if (node.nodeType == 1) nodo de retorno;
      return null;
    },

    / / FUNCIONES DE TOKEN
    tagName: function (nodos, raíz, TagName, combinadores) {
      var uTagName tagName.toUpperCase = ();
      resultados var = [], h = Selector.handlers;
      if (nodos) {
        if (combinador) {
          / / Fastlane para combinadores descendientes comunes
          si (== combinador "descendiente") {
            for (var i = 0, el nodo, nodo nodos = [i], i + +)
              h.concat (resultados, node.getElementsByTagName (tagName));
            devuelven resultados;
          } Else = ganglios este [combinador] (nodos);
          if (tagName == "*") nodos de retorno;
        }
        for (var i = 0, el nodo, nodo nodos = [i], i + +)
          if (node.tagName.toUpperCase () === uTagName) results.push (nodo);
        devuelven resultados;
      } Else retorno root.getElementsByTagName (tagName);
    },

    Identificación: function (nodos, raíz, id, combinadores) {
      var TargetNode = $ (id), h = Selector.handlers;
      si es una declaración [] (TargetNode!);
      if (nodos raíz && == documento!) return [TargetNode];
      if (nodos) {
        if (combinador) {
          si (== combinador "niño") {
            for (var i = 0, el nodo, nodo nodos = [i], i + +)
              if (nodo == targetNode.parentNode) return [TargetNode];
          } Else if (combinador == 'descendiente') {
            for (var i = 0, el nodo, nodo nodos = [i], i + +)
              if (Element.descendantOf (TargetNode, nodo)) return [TargetNode];
          } Else if (combinador == 'adyacente') {
            for (var i = 0, el nodo, nodo nodos = [i], i + +)
              if (Selector.handlers.previousElementSibling (TargetNode) == nodo)
                volver [TargetNode];
          } Else nodos = h [combinador] (nodos);
        }
        for (var i = 0, el nodo, nodo nodos = [i], i + +)
          if (nodo == TargetNode) return [TargetNode];
        volver [];
      }
      return (&& TargetNode Element.descendantOf (TargetNode, raíz))? [TargetNode]: [];
    },

    className: function (nodos, raíz, ClassName, combinadores) {
      if (&& nodos combinador) = esta nodos [combinador] (nodos);
      Selector.handlers.byClassName retorno (nodos, raíz, className);
    },

    byClassName: function (nodos, raíz, className) {
      si los ganglios Selector.handlers.descendant = ([root]) (ganglios!);
      var aguja = '' + className + '';
      for (var i = 0, resultado = [], nodo, nodeClassName; nodo nodos = [i], i + +) {
        nodeClassName = node.className;
        if (nodeClassName.length == 0) continuar;
        si (== nodeClassName className | |. ('' + nodeClassName + '') incluyen (aguja))
          results.push (nodo);
      }
      devuelven resultados;
    },

    attrPresence: function (nodos, raíz, combinadores, attr) {
      si los nodos = root.getElementsByTagName ("*") (nodos!);
      if (&& nodos combinador) = esta nodos [combinador] (nodos);
      resultados var = [];
      for (var i = 0, el nodo, nodo nodos = [i], i + +)
        if (Element.hasAttribute (nodo, attr)) results.push (nodo);
      devuelven resultados;
    },

    attr: function (nodos, raíz, attr, valor, operador, combinadores) {
      si los nodos = root.getElementsByTagName ("*") (nodos!);
      if (&& nodos combinador) = esta nodos [combinador] (nodos);
      var = manejador Selector.operators [operador], resultados = [];
      for (var i = 0, el nodo, nodo = ganglios [i], i + +) {
        var nodeValue Element.readAttribute = (nodo, attr);
        if (nodeValue === null) continuar;
        if (handler (nodeValue, valor)) results.push (nodo);
      }
      devuelven resultados;
    },

    seudo: function (nodos, name, value, raíz, combinadores) {
      if (&& nodos combinador) = esta nodos [combinador] (nodos);
      si los nodos = root.getElementsByTagName ("*") (nodos!);
      volver Selector.pseudos [nombre] (nodos, el valor, la raíz);
    }
  },

  pseudos: {
    'First-child': function (nodos, el valor, la raíz) {
      for (var i = 0, resultado = [], nodo, nodo = ganglios [i], i + +) {
        if (Selector.handlers.previousElementSibling (nodo)) continuar;
          results.push (nodo);
      }
      devuelven resultados;
    },
    'Last-child': function (nodos, el valor, la raíz) {
      for (var i = 0, resultado = [], nodo, nodo = ganglios [i], i + +) {
        if (Selector.handlers.nextElementSibling (nodo)) continuar;
          results.push (nodo);
      }
      devuelven resultados;
    },
    'Hijo único': function (nodos, el valor, la raíz) {
      var h = Selector.handlers;
      for (var i = 0, resultado = [], nodo, nodo nodos = [i], i + +)
        if (! h.previousElementSibling (nodo) &&! h.nextElementSibling (nodo))
          results.push (nodo);
      devuelven resultados;
    },
    'Nth-child': function (nodos, fórmula, raíz) {
      volver Selector.pseudos.nth (nodos, fórmula, raíces);
    },
    'N-ésimo last-child': function (nodos, fórmula, raíz) {
      volver Selector.pseudos.nth (nodos, fórmula, raíz, true);
    },
    'Nth-of-type': function (nodos, fórmula, raíz) {
      volver Selector.pseudos.nth (nodos, fórmula, raíz, false, true);
    },
    'N-ésimo pasado-of-type': function (nodos, fórmula, raíz) {
      volver Selector.pseudos.nth (nodos, fórmula, raíz, true, true);
    },
    "El primero de tipo ': function (nodos, fórmula, raíz) {
      volver Selector.pseudos.nth (nodos, "1", la raíz, false, true);
    },
    'Last-of-type': function (nodos, fórmula, raíz) {
      volver Selector.pseudos.nth (nodos, "1", la raíz, true, true);
    },
    'Sólo-de-type': function (nodos, fórmula, raíz) {
      var p = Selector.pseudos;
      regresar p ['última de tipo'] (p ['primero de tipo'] nodos (, fórmula, raíz), fórmula, raíz);
    },

    / / Maneja el an + b lógica
    getIndices: function (a, b, total) {
      if (a == 0) return b> 0? [B]: [];
      return $ R (1 en total). inyectar ([], function (memo, i) {
        if (0 == (i - b)% a && (i - b) / a> = 0) memo.push (i);
        volver memo;
      });
    },

    / / Maneja nth (-último)-niño, nth (-último)-de-tipo, y (primera | última)-de-tipo
    nth: function (nodos, fórmula, raíz, inversas, OfType) {
      if (nodes.length == 0) return [];
      if (fórmula == 'incluso'), la fórmula = '2 +0' n;
      si (== fórmula "impar"), la fórmula = '2 +1' n;
      var h = Selector.handlers, resultados = [], indexado = [], m;
      h.mark (nodos);
      for (var i = 0, el nodo, nodo = ganglios [i], i + +) {
        if (! node.parentNode._countedByPrototype) {
          h.index (node.parentNode, marcha atrás, OfType);
          indexed.push (node.parentNode);
        }
      }
      if (formula.match (/ ^ \ d + $ /)) {/ / sólo un número
        fórmula = Number (fórmula);
        for (var i = 0, el nodo, nodo nodos = [i], i + +)
          si (== node.nodeIndex fórmula) results.push (nodo);
      } Else if (m = formula.match (/ ^ (- \ d *) n ((? [+ -]?) (\ D +)) /)) {/ / un b +
        if (m [1] == "-") m [1] = -1;
        var a = m [1]? Número (m [1]): 1;
        var b = m [2]? Número (m [2]): 0;
        índices var = Selector.pseudos.getIndices (a, b, nodes.length);
        for (var i = 0, el nodo, l = indices.length; nodo nodos = [i], i + +) {
          for (var j = 0; j <l; j + +)
            si (== node.nodeIndex índices [j]) results.push (nodo);
        }
      }
      h.unmark (nodos);
      h.unmark (indexado);
      devuelven resultados;
    },

    'Vacío': function (nodos, el valor, la raíz) {
      for (var i = 0, resultado = [], nodo, nodo = ganglios [i], i + +) {
        / / IE trata a comentarios como nodos de elemento
        if (node.tagName == | | (&& node.firstChild node.innerHTML.match (/ ^ \ s * $ /)) '!'!) continuar;
        results.push (nodo);
      }
      devuelven resultados;
    },

    'No': function (nodos, selectores, raíz) {
      var h = Selector.handlers, selectorType, m;
      exclusiones var = new Selector (selector) findElements (root).;
      h.mark (exclusiones);
      for (var i = 0, resultado = [], nodo, nodo nodos = [i], i + +)
        si results.push (nodo) (node._countedByPrototype!);
      h.unmark (exclusiones);
      devuelven resultados;
    },

    'Enabled': function (nodos, el valor, la raíz) {
      for (var i = 0, resultado = [], nodo, nodo nodos = [i], i + +)
        si results.push (nodo) (node.disabled!);
      devuelven resultados;
    },

    'Desactivado': function (nodos, el valor, la raíz) {
      for (var i = 0, resultado = [], nodo, nodo nodos = [i], i + +)
        if (node.disabled) results.push (nodo);
      devuelven resultados;
    },

    "Marcado": function (nodos, el valor, la raíz) {
      for (var i = 0, resultado = [], nodo, nodo nodos = [i], i + +)
        if (node.checked) results.push (nodo);
      devuelven resultados;
    }
  },

  operadores: {
    '=': Function (nv, v) {return nv == v;}
    '! =': Function (nv, v) {return nv = v;},
    '^ =': Function (nv, v) {return nv.startsWith (v);},
    '$ =': Function (nv, v) {return nv.endsWith (v);}
    '* =': Function (nv, v) {return nv.include (v);}
    '~ =': Function (nv, v) {return ('' + nv + '') include ('' + v + '');.}
    '| =': Function (nv, v) {return ('-' + nv.toUpperCase () + '-') include ('-' + v.toUpperCase () + '-'),.}
  },

  división: function (expresión) {
    expresiones var = [];
    expression.scan (/ (([\ w #:. ~> + () \ s-] [.? * \] + | | \ * \) +) \ s * (, | $) /, function (m ) {
      expressions.push (. m [1] tira ());
    });
    volver expresiones;
  },

  matchElements: function (elementos, expresión) {
    var partidos = $ $ (expresión), h = Selector.handlers;
    h.mark (partidos);
    for (var i = 0, resultado = [], elemento, elemento elementos = [i], i + +)
      if (element._countedByPrototype) results.push (elemento);
    h.unmark (partidos);
    devuelven resultados;
  },

  findElement: function (elementos, expresión, index) {
    if (Object.isNumber (expresión)) {
      index = expresión; expresión = false;
    }
    Selector.matchElements retorno (elementos, expresión | | '*') [Índice | | 0];
  },

  findChildElements: function (elemento, expresiones) {
    expresiones = Selector.split (expressions.join (','));
    resultados var = [], h = Selector.handlers;
    for (var i = 0, l = expressions.length, selector; i <l; i + +) {
      selector = new Selector (expresiones [i] strip ().);
      h.concat (resultados, selector.findElements (elemento));
    }
    return (l> 1)? h.unique (resultados): los resultados;
  }
});

si (Prototype.Browser.IE) {
  Object.extend (Selector.handlers, {
    / / Devuelve IE comentar nodos en getElementsByTagName ("*").
    / / Ellos Filtro de Aire.
    concat: función (a, b) {
      for (var i = 0, el nodo, nodo = b [i]; i + +)
        if (node.tagName == "") a.push (nodo);
      volver a;
    },

    / / IE indebidamente serializa en _countedByPrototype (interior | exterior) HTML.
    desmarcar: function (nodos) {
      for (var i = 0, el nodo, nodo nodos = [i], i + +)
        node.removeAttribute ('_countedByPrototype');
      los nodos de retorno;
    }
  });
}

función $ $ () {
  volver Selector.findChildElements (documento, $ A (argumentos));
}
var form = {
  reposición: function (forma) {
    . $ (Forma) reset ();
    Devuelva el formulario;
  },

  serializeElements: function (elementos, opciones) {
    if (typeof opciones = 'objeto'!) Opciones = {hash:! Opciones};
    else if (Object.isUndefined (options.hash)) options.hash = true;
    clave var, valor, presentado = false, presentar options.submit =;

    var data = elements.inject ({}, function (resultado, elemento) {
      if (! element.name && element.disabled) {
        clave = element.name;. valor = $ (elemento) getValue ();
        if (valor = && nulos (element.type = 'submit' | |!!! (&& presentados
            presentar == false &&! (enviar | | clave == enviar) && (presentado = true)))) {
          if (clave en el resultado) {
            / / Llave ya está presente, construir una matriz de valores
            si el resultado [clave] = [result [clave]] (Object.isArray (resultado [clave])!);
            result [clave] push (valor).;
          }
          otro resultado [clave] = valor;
        }
      }
      return resultado;
    });

    volver options.hash? datos: Object.toQueryString (datos);
  }
};

Form.Methods = {
  serializar: function (forma, options) {
    volver Form.serializeElements (Form.getElements (forma), opciones);
  },

  getElements: function (forma) {
    A return $ ($ (formulario). getElementsByTagName ('*')). inyectar ([],
      función (elementos, hijo) {
        if (Form.Element.Serializers [child.tagName.toLowerCase ()])
          elements.push (Element.extend (niño));
        devolver elementos;
      }
    );
  },

  GetInputs: function (forma, typeName, nombre) {
    form = $ (formulario);
    insumos var = form.getElementsByTagName ('input');

    (¡! && TypeName nombre) si devolver $ A (entradas) map (Element.extend).;

    for (var i = 0, matchingInputs = [], longitud = inputs.length; longitud i <; i + +) {
      var = input entradas [i];
      if ((&& = TypeName input.type typeName) | |! (nombre input.name && name =))
        continuar;
      matchingInputs.push (Element.extend (entrada));
    }

    volver matchingInputs;
  },

  desactivar: function (forma) {
    form = $ (formulario);
    Form.getElements (forma) invocar ('desactivar').;
    Devuelva el formulario;
  },

  permitir: function (forma) {
    form = $ (formulario);
    . Form.getElements (forma) invocar ('enable');
    Devuelva el formulario;
  },

  findFirstElement: function (forma) {
    var elementos = $ (forma). getElements (). findAll (función (elemento) {
      volver "oculto" = && element.type element.disabled!;
    });
    var firstByIndex elements.findAll = (function (elemento) {
      element.hasAttribute retorno ('tabIndex') && element.tabIndex> = 0;
    ..}) SortBy (function (elemento) {return} element.tabIndex) primero ();

    volver firstByIndex? firstByIndex: elements.find (function (elemento) {
      retorno ['input', 'select', 'textarea'] include (element.tagName.toLowerCase ()).;
    });
  },

  focusFirstElement: function (forma) {
    form = $ (formulario);
    . form.findFirstElement () activar ();
    Devuelva el formulario;
  },

  petición: function (forma, options) {
    form = $ (forma), opciones = Object.clone (opciones | | {});

    params var = options.parameters, acción form.readAttribute = ("acción") | |'';
    if (action.blank ()) action = window.location.href;
    options.parameters = Form.serialize (true);

    if (params) {
      if (Object.isString (params)) params = params.toQueryParams ();
      Object.extend (options.parameters, params);
    }

    if (form.hasAttribute ("método") &&! options.method)
      options.method = form.method;

    return new Ajax.Request (acción, opciones);
  }
};

/ * ------------------------------------------------ -------------------------- * /

Form.Element = {
  foco: function (elemento) {
    . $ (Elemento) focus ();
    volver elemento;
  },

  seleccionar: function (elemento) {
    . $ (Elemento) select ();
    volver elemento;
  }
};

Form.Element.Methods = {
  serializar: function (elemento) {
    elemento = $ (elemento);
    if (! element.name && element.disabled) {
      var = valor element.getValue ();
      if (valor! = undefined) {
        var par = {};
        par [element.name] = valor;
        volver Object.toQueryString (par);
      }
    }
    volver'';
  },

  getValue: function (elemento) {
    elemento = $ (elemento);
    var method = element.tagName.toLowerCase ();
    volver Form.Element.Serializers [método] (elemento);
  },

  setValue: function (elemento, valor) {
    elemento = $ (elemento);
    var method = element.tagName.toLowerCase ();
    Form.Element.Serializers [método] (elemento, valor);
    volver elemento;
  },

  claro: function (elemento) {
    $ (Elemento) = valor''.;
    volver elemento;
  },

  presente: function (elemento) {
    return $ (elemento) = valor''.!;
  },

  activate: function (elemento) {
    elemento = $ (elemento);
    try {
      element.focus ();
      if (&& Element.select (element.tagName.toLowerCase () = 'entrada'! | |
          ! ['Botón', 'reset', 'enviar']. Incluyen (element.type)))
        Element.select ();
    } Catch (e) {}
    volver elemento;
  },

  desactivar: function (elemento) {
    elemento = $ (elemento);
    element.blur ();
    element.disabled = true;
    volver elemento;
  },

  permitir: function (elemento) {
    elemento = $ (elemento);
    element.disabled = false;
    volver elemento;
  }
};

/ * ------------------------------------------------ -------------------------- * /

var = El campo Form.Element;
var $ F = Form.Element.Methods.getValue;

/ * ------------------------------------------------ -------------------------- * /

Form.Element.Serializers = {
  entrada: function (elemento, valor) {
    interruptor (element.type.toLowerCase ()) {
      caso 'checkbox':
      caso "radio":
        volver Form.Element.Serializers.inputSelector (elemento, valor);
      por defecto:
        volver Form.Element.Serializers.textarea (elemento, valor);
    }
  },

  inputSelector: function (elemento, valor) {
    if (Object.isUndefined (valor)) volver element.checked? element.value: null;
    ELSE = valor element.checked!;
  },

  textarea: function (elemento, valor) {
    if (Object.isUndefined (valor)) return element.value;
    más element.value = valor;
  },

  seleccionar: function (element, index) {
    if (Object.isUndefined (index))
      devuelva este [element.type == 'seleccionar uno "?
        'SelectOne': 'SelectMany'] (elemento);
    else {
      var opt, valor, = single Object.isArray (índice)!;
      for (var i = 0, longitud = element.length; longitud i <; i + +) {
        opt = element.options [i];
        valor = this.optionValue (opt);
        if (individual) {
          if (index == valor) {
            opt.selected = true;
            volver;
          }
        }
        más opt.selected index.include = (valor);
      }
    }
  },

  SelectOne: function (elemento) {
    var index = element.selectedIndex;
    volver index> = 0? this.optionValue (element.options [indice]): null;
  },

  SelectMany: function (elemento) {
    var valores, longitud = element.length;
    si return null (longitud!);

    for (var i = 0, los valores = []; i <longitud, i + +) {
      var opt = element.options [i];
      if (opt.selected) values.push (this.optionValue (opt));
    }
    devolver valores;
  },

  optionvalue: function (opcional) {
    / / Extender elemento porque hasAttribute no puede ser nativa
    volver Element.extend (opt). hasAttribute ('valor')? opt.value: opt.text;
  }
};

/ * ------------------------------------------------ -------------------------- * /

Abstract.TimedObserver = Class.create (PeriodicalExecuter, {
  initialize: function ($ super, elemento, frecuencia, callback) {
    $ Super (devolución de llamada, frecuencia);
    this.element = $ (elemento);
    this.lastValue this.getValue = ();
  },

  ejecute: function () {
    var = valor this.getValue ();
    if (Object.isString (this.lastValue) Object.isString && (valor)?
        this.lastValue valor =:! String (this.lastValue) String = (valor)) {
      this.callback (this.element, valor);
      this.lastValue = valor;
    }
  }
});

Form.Element.Observer = Class.create (Abstract.TimedObserver, {
  getValue: function () {
    retorno Form.Element.getValue (this.element);
  }
});

Form.Observer = Class.create (Abstract.TimedObserver, {
  getValue: function () {
    volver Form.serialize (this.element);
  }
});

/ * ------------------------------------------------ -------------------------- * /

Abstract.EventObserver = Class.create ({
  initialize: function (elemento, callback) {
    this.element = $ (elemento);
    this.callback = callback;

    this.lastValue this.getValue = ();
    if (this.element.tagName.toLowerCase () == 'form')
      this.registerFormCallbacks ();
    más
      this.registerCallback (this.element);
  },

  onElementEvent: function () {
    var = valor this.getValue ();
    if (this.lastValue! = valor) {
      this.callback (this.element, valor);
      this.lastValue = valor;
    }
  },

  registerFormCallbacks: function () {
    Form.getElements (this.element) cada uno (this.registerCallback, este).;
  },

  RegisterCallback: function (elemento) {
    si (element.type) {
      interruptor (element.type.toLowerCase ()) {
        caso 'checkbox':
        caso "radio":
          Event.observe (element, 'click', this.onElementEvent.bind (this));
          break;
        por defecto:
          Event.observe (element, "cambio", this.onElementEvent.bind (this));
          break;
      }
    }
  }
});

Form.Element.EventObserver = Class.create (Abstract.EventObserver, {
  getValue: function () {
    retorno Form.Element.getValue (this.element);
  }
});

Form.EventObserver = Class.create (Abstract.EventObserver, {
  getValue: function () {
    volver Form.serialize (this.element);
  }
});
if (!window.Event) var Event = { };

Object.extend(Event, {
  KEY_BACKSPACE: 8,
  KEY_TAB:       9,
  KEY_RETURN:   13,
  KEY_ESC:      27,
  KEY_LEFT:     37,
  KEY_UP:       38,
  KEY_RIGHT:    39,
  KEY_DOWN:     40,
  KEY_DELETE:   46,
  KEY_HOME:     36,
  KEY_END:      35,
  KEY_PAGEUP:   33,
  KEY_PAGEDOWN: 34,
  KEY_INSERT:   45,

  cache: { },

  relatedTarget: function(event) {
    var element;
    switch(event.type) {
      case 'mouseover': element = event.fromElement; break;
      case 'mouseout':  element = event.toElement;   break;
      default: return null;
    }
    return Element.extend(element);
  }
});

Event.Methods = (function() {
  var isButton;

  if (Prototype.Browser.IE) {
    var buttonMap = { 0: 1, 1: 4, 2: 2 };
    isButton = function(event, code) {
      return event.button == buttonMap[code];
    };

  } else if (Prototype.Browser.WebKit) {
    isButton = function(event, code) {
      switch (code) {
        case 0: return event.which == 1 && !event.metaKey;
        case 1: return event.which == 1 && event.metaKey;
        default: return false;
      }
    };

  } else {
    isButton = function(event, code) {
      return event.which ? (event.which === code + 1) : (event.button === code);
    };
  }

  return {
    isLeftClick:   function(event) { return isButton(event, 0) },
    isMiddleClick: function(event) { return isButton(event, 1) },
    isRightClick:  function(event) { return isButton(event, 2) },

    element: function(event) {
      var node = Event.extend(event).target;
      return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node);
    },

    findElement: function(event, expression) {
      var element = Event.element(event);
      if (!expression) return element;
      var elements = [element].concat(element.ancestors());
      return Selector.findElement(elements, expression, 0);
    },

    pointer: function(event) {
      return {
        x: event.pageX || (event.clientX +
          (document.documentElement.scrollLeft || document.body.scrollLeft)),
        y: event.pageY || (event.clientY +
          (document.documentElement.scrollTop || document.body.scrollTop))
      };
    },

    pointerX: function(event) { return Event.pointer(event).x },
    pointerY: function(event) { return Event.pointer(event).y },

    stop: function(event) {
      Event.extend(event);
      event.preventDefault();
      event.stopPropagation();
      event.stopped = true;
    }
  };
})();

Event.extend = (function() {
  var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
    m[name] = Event.Methods[name].methodize();
    return m;
  });

  if (Prototype.Browser.IE) {
    Object.extend(methods, {
      stopPropagation: function() { this.cancelBubble = true },
      preventDefault:  function() { this.returnValue = false },
      inspect: function() { return "[object Event]" }
    });

    return function(event) {
      if (!event) return false;
      if (event._extendedByPrototype) return event;

      event._extendedByPrototype = Prototype.emptyFunction;
      var pointer = Event.pointer(event);
      Object.extend(event, {
        target: event.srcElement,
        relatedTarget: Event.relatedTarget(event),
        pageX:  pointer.x,
        pageY:  pointer.y
      });
      return Object.extend(event, methods);
    };

  } else {
    Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__;
    Object.extend(Event.prototype, methods);
    return Prototype.K;
  }
})();

Object.extend(Event, (function() {
  var cache = Event.cache;

  function getEventID(element) {
    if (element._prototypeEventID) return element._prototypeEventID[0];
    arguments.callee.id = arguments.callee.id || 1;
    return element._prototypeEventID = [++arguments.callee.id];
  }

  function getDOMEventName(eventName) {
    if (eventName && eventName.include(':')) return "dataavailable";
    return eventName;
  }

  function getCacheForID(id) {
    return cache[id] = cache[id] || { };
  }

  function getWrappersForEventName(id, eventName) {
    var c = getCacheForID(id);
    return c[eventName] = c[eventName] || [];
  }

  function createWrapper(element, eventName, handler) {
    var id = getEventID(element);
    var c = getWrappersForEventName(id, eventName);
    if (c.pluck("handler").include(handler)) return false;

    var wrapper = function(event) {
      if (!Event || !Event.extend ||
        (event.eventName && event.eventName != eventName))
          return false;

      Event.extend(event);
      handler.call(element, event);
    };

    wrapper.handler = handler;
    c.push(wrapper);
    return wrapper;
  }

  function findWrapper(id, eventName, handler) {
    var c = getWrappersForEventName(id, eventName);
    return c.find(function(wrapper) { return wrapper.handler == handler });
  }

  function destroyWrapper(id, eventName, handler) {
    var c = getCacheForID(id);
    if (!c[eventName]) return false;
    c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
  }

  function destroyCache() {
    for (var id in cache)
      for (var eventName in cache[id])
        cache[id][eventName] = null;
  }

  if (window.attachEvent) {
    window.attachEvent("onunload", destroyCache);
  }

  return {
    observe: function(element, eventName, handler) {
      element = $(element);
      var name = getDOMEventName(eventName);

      var wrapper = createWrapper(element, eventName, handler);
      if (!wrapper) return element;

      if (element.addEventListener) {
        element.addEventListener(name, wrapper, false);
      } else {
        element.attachEvent("on" + name, wrapper);
      }

      return element;
    },

    stopObserving: function(element, eventName, handler) {
      element = $(element);
      var id = getEventID(element), name = getDOMEventName(eventName);

      if (!handler && eventName) {
        getWrappersForEventName(id, eventName).each(function(wrapper) {
          element.stopObserving(eventName, wrapper.handler);
        });
        return element;

      } else if (!eventName) {
        Object.keys(getCacheForID(id)).each(function(eventName) {
          element.stopObserving(eventName);
        });
        return element;
      }

      var wrapper = findWrapper(id, eventName, handler);
      if (!wrapper) return element;

      if (element.removeEventListener) {
        element.removeEventListener(name, wrapper, false);
      } else {
        element.detachEvent("on" + name, wrapper);
      }

      destroyWrapper(id, eventName, handler);

      return element;
    },

    fire: function(element, eventName, memo) {
      element = $(element);
      if (element == document && document.createEvent && !element.dispatchEvent)
        element = document.documentElement;

      var event;
      if (document.createEvent) {
        event = document.createEvent("HTMLEvents");
        event.initEvent("dataavailable", true, true);
      } else {
        event = document.createEventObject();
        event.eventType = "ondataavailable";
      }

      event.eventName = eventName;
      event.memo = memo || { };

      if (document.createEvent) {
        element.dispatchEvent(event);
      } else {
        element.fireEvent(event.eventType, event);
      }

      return Event.extend(event);
    }
  };
})());

Object.extend(Event, Event.Methods);

Element.addMethods({
  fire:          Event.fire,
  observe:       Event.observe,
  stopObserving: Event.stopObserving
});

Object.extend(document, {
  fire:          Element.Methods.fire.methodize(),
  observe:       Element.Methods.observe.methodize(),
  stopObserving: Element.Methods.stopObserving.methodize(),
  loaded:        false
});

(function() {
  /* Support for the DOMContentLoaded event is based on work by Dan Webb,
     Matthias Miller, Dean Edwards and John Resig. */

  var timer;

  function fireContentLoadedEvent() {
    if (document.loaded) return;
    if (timer) window.clearInterval(timer);
    document.fire("dom:loaded");
    document.loaded = true;
  }

  if (document.addEventListener) {
    if (Prototype.Browser.WebKit) {
      timer = window.setInterval(function() {
        if (/loaded|complete/.test(document.readyState))
          fireContentLoadedEvent();
      }, 0);

      Event.observe(window, "load", fireContentLoadedEvent);

    } else {
      document.addEventListener("DOMContentLoaded",
        fireContentLoadedEvent, false);
    }

  } else {
    document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
    $("__onDOMContentLoaded").onreadystatechange = function() {
      if (this.readyState == "complete") {
        this.onreadystatechange = null;
        fireContentLoadedEvent();
      }
    };
  }
})();
/*------------------------------- DEPRECATED -------------------------------*/

Hash.toQueryString = Object.toQueryString;

var Toggle = { display: Element.toggle };

Element.Methods.childOf = Element.Methods.descendantOf;

var Insertion = {
  Before: function(element, content) {
    return Element.insert(element, {before:content});
  },

  Top: function(element, content) {
    return Element.insert(element, {top:content});
  },

  Bottom: function(element, content) {
    return Element.insert(element, {bottom:content});
  },

  After: function(element, content) {
    return Element.insert(element, {after:content});
  }
};

var $continue = new Error('"throw $continue" is deprecated, use "return" instead');

// This should be moved to script.aculo.us; notice the deprecated methods
// further below, that map to the newer Element methods.
var Position = {
  // set to true if needed, warning: firefox performance problems
  // NOT neeeded for page scrolling, only if draggable contained in
  // scrollable elements
  includeScrollOffsets: false,

  // must be called before calling withinIncludingScrolloffset, every time the
  // page is scrolled
  prepare: function() {
    this.deltaX =  window.pageXOffset
                || document.documentElement.scrollLeft
                || document.body.scrollLeft
                || 0;
    this.deltaY =  window.pageYOffset
                || document.documentElement.scrollTop
                || document.body.scrollTop
                || 0;
  },

  // caches x/y coordinate pair to use with overlap
  within: function(element, x, y) {
    if (this.includeScrollOffsets)
      return this.withinIncludingScrolloffsets(element, x, y);
    this.xcomp = x;
    this.ycomp = y;
    this.offset = Element.cumulativeOffset(element);

    return (y >= this.offset[1] &&
            y <  this.offset[1] + element.offsetHeight &&
            x >= this.offset[0] &&
            x <  this.offset[0] + element.offsetWidth);
  },

  withinIncludingScrolloffsets: function(element, x, y) {
    var offsetcache = Element.cumulativeScrollOffset(element);

    this.xcomp = x + offsetcache[0] - this.deltaX;
    this.ycomp = y + offsetcache[1] - this.deltaY;
    this.offset = Element.cumulativeOffset(element);

    return (this.ycomp >= this.offset[1] &&
            this.ycomp <  this.offset[1] + element.offsetHeight &&
            this.xcomp >= this.offset[0] &&
            this.xcomp <  this.offset[0] + element.offsetWidth);
  },

  // within must be called directly before
  overlap: function(mode, element) {
    if (!mode) return 0;
    if (mode == 'vertical')
      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
        element.offsetHeight;
    if (mode == 'horizontal')
      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
        element.offsetWidth;
  },

  // Deprecation layer -- use newer Element methods now (1.5.2).

  cumulativeOffset: Element.Methods.cumulativeOffset,

  positionedOffset: Element.Methods.positionedOffset,

  absolutize: function(element) {
    Position.prepare();
    return Element.absolutize(element);
  },

  relativize: function(element) {
    Position.prepare();
    return Element.relativize(element);
  },

  realOffset: Element.Methods.cumulativeScrollOffset,

  offsetParent: Element.Methods.getOffsetParent,

  page: Element.Methods.viewportOffset,

  clone: function(source, target, options) {
    options = options || { };
    return Element.clonePosition(target, source, options);
  }
};

/*--------------------------------------------------------------------------*/

if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
  function iter(name) {
    return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
  }

  instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
  function(element, className) {
    className = className.toString().strip();
    var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
    return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
  } : function(element, className) {
    className = className.toString().strip();
    var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
    if (!classNames && !className) return elements;

    var nodes = $(element).getElementsByTagName('*');
    className = ' ' + className + ' ';

    for (var i = 0, child, cn; child = nodes[i]; i++) {
      if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
          (classNames && classNames.all(function(name) {
            return !name.toString().blank() && cn.include(' ' + name + ' ');
          }))))
        elements.push(Element.extend(child));
    }
    return elements;
  };

  return function(className, parentElement) {
    return $(parentElement || document.body).getElementsByClassName(className);
  };
}(Element.Methods);

/*--------------------------------------------------------------------------*/

Element.ClassNames = Class.create();
Element.ClassNames.prototype = {
  initialize: function(element) {
    this.element = $(element);
  },

  _each: function(iterator) {
    this.element.className.split(/\s+/).select(function(name) {
      return name.length > 0;
    })._each(iterator);
  },

  set: function(className) {
    this.element.className = className;
  },

  add: function(classNameToAdd) {
    if (this.include(classNameToAdd)) return;
    this.set($A(this).concat(classNameToAdd).join(' '));
  },

  remove: function(classNameToRemove) {
    if (!this.include(classNameToRemove)) return;
    this.set($A(this).without(classNameToRemove).join(' '));
  },

  toString: function() {
    return $A(this).join(' ');
  }
};

Object.extend(Element.ClassNames.prototype, Enumerable);

/*--------------------------------------------------------------------------*/

Element.addMethods();